寫了一套優雅接口之后,領導讓我給大家講講這背后的技術原理


Hello,各位小伙伴們,早上好~

上周文章年輕人不講武德,竟然重構出這么優雅后台 API 接口我們使用 @ControllerAdviceResponseBodyAdvice 重構后端的 API 接口,降低了復雜度,減少了重復代碼,后續接口開發非常簡潔優雅。

知其然而知其所以然,今天這篇文章來聊聊這個注解背后的原理,讓我們徹底掌握這個注解,避免后續踩坑。

另外,有個小伙伴看完上篇文章,覺得這個注解的跟 Spring Interceptor 功能很類似,再加上之前還學習了 Servlet 體系 Filter 功能,不知道這幾個有什么區別,感覺很混亂。

所以今天這篇文章下面兩個部分出發,詳細解釋一下。

  1. @ControllerAdviceResponseBodyAdvice 注解原理
  2. FilterInterceptorResponseBodyAdvice 區別

歡迎關注我的公眾號:程序通事,獲得日常干貨推送。如果您對我的專題內容感興趣,也可以關注我的博客:studyidea.cn

從源碼解析背后的原理

上篇文章中我們看到 ResponseBodyAdvice的子類使用 @ControllerAdvice注解,大家有沒有好奇,如果我將@ControllerAdvice換成 @Controller 注解,還能達到上篇文章的效果嗎?

感興趣的小伙伴可以自己嘗試下,這里小黑哥自己告訴大家結果了,實際測試結果是不行的。

那為什么一定要與@ControllerAdvice 搭配才會生效?

首先我們先查看一下 @ControllerAdvice 的源碼:

image-20201128152447563

可以看到這個注解上還存在一個我們非常熟悉的 @Component 注解。這里我們可以將 @ControllerAdvice 理解成@Component 子類,所以其修飾的類也會成為 Spring 中 Bean

ps:大家可以看下 @Controller/@Service/@Repository,其實也是這個原理。

Spring 容器初始化過程,如果掃描到 @ControllerAdvice 注解,將會將其生成一個 ControllerAdviceBean Bean。

這個過程代碼主要位於 RequestMappingHandlerAdapter#initControllerAdviceCache:

這段代碼主要分為兩步:

第一步使用 ControllerAdviceBean#findAnnotatedBeans獲取所有被 @ControllerAdvice修飾的類。

第二步將所有實現了ResponseBodyAdvice 接口的 Bean 放入到 requestResponseBodyAdviceBeans 集合中,后續將會使用該集合。

這就解釋了為什么實現 ResponseBodyAdvice接口的子類一定要與@ControllerAdvice一起使用的原因了。

接下來我們來看下 ResponseBodyAdvice 的執行流程。

這里教給大家一個代碼調試的小技巧,當我們不知道一個類在源碼中如何被調用的時候,我們可以使用 IDEA 代碼調試功能,然后查看代碼調用棧。

如上面的所示,我們可以很清楚觀察 ResponseBodyAdvice 調用關系。這里的類調用關系相對還是比較復雜,下面給大家簡化一下。

前面的邏輯就不說了,就是 Spring MVC 通用流程。重點邏輯位於 RequestResponseBodyAdviceChain,我們具體看下源碼:

嗯吶嗯吶,請忽略上圖的 ③

其實邏輯非常簡單,遍歷所有的 ResponseBodyAdvice 的子類,首先調用其 supports判斷是否支持,如果支持的調用的 beforeBodyWrite修改返回信息。

FilterInterceptorResponseBodyAdvice 區別

Filter屬於 Servlet 組件,所有請求將會先進入 Filter ,判斷通過之后才會在進入到真正的具體的請求中。

上圖代表是用 Spring MVC 的一個 Web 項目,所有請求將會先進入到 Filter,通過之后才會進入到 SpringMVC 中最重要的組件 DispatchServlet

Interceptor 是 SpringMVC 的組件,它的作用實際上與 Filter類似, 只不過的它的作用是位於自定義的 Controller 前后。

不管是 Filter 還是 Interceptor,它們的作用方法域內只能拿到 ServletResponse 的參數,這個時候返回值已經被寫入 ServletResponse,我們很難再去修改。

ResponseBodyAdvice作用時機位於寫入之前,所以這個時候可以很容易拿到原值進行修改。

總結

SpringMVC 初始化的過程中,將會掃描所有帶有 @ControllerAdvice注解的類,將其生成為 ControllerAdviceBean。如果這類剛好為 ResponseBodyAdvice接口的子類,Spring 將會為其單獨保存起來,后續將會封裝到的 RequestResponseBodyAdviceChain,使用責任鏈的模式對請求、響應進行處理。

最后我們解釋了一下 FilterInterceptorResponseBodyAdvice區別,從作用范圍上來講:

Filter>Interceptor>ResponseBodyAdvice

但是前兩者沒辦法修改返回值(時機太晚),只有后者才可以真正在返回值返回之前做到修改。

好了,今天文章就到這里了,下次我們分享一下如何寫出優雅的 Dubbo 接口,下次見。

歡迎關注我的公眾號:程序通事,獲得日常干貨推送。如果您對我的專題內容感興趣,也可以關注我的博客:studyidea.cn


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM