Hello,各位小夥伴們,早上好~
上週文章年輕人不講武德,竟然重構出這麼優雅後臺 API 介面我們使用 @ControllerAdvice
與 ResponseBodyAdvice
重構後端的 API 介面,降低了複雜度,減少了重複程式碼,後續介面開發非常簡潔優雅。
知其然而知其所以然,今天這篇文章來聊聊這個註解背後的原理,讓我們徹底掌握這個註解,避免後續踩坑。
另外,有個小夥伴看完上篇文章,覺得這個註解的跟 Spring Interceptor
功能很類似,再加上之前還學習了 Servlet
體系 Filter
功能,不知道這幾個有什麼區別,感覺很混亂。
所以今天這篇文章下面兩個部分出發,詳細解釋一下。
@ControllerAdvice
與ResponseBodyAdvice
註解原理Filter
,Interceptor
,ResponseBodyAdvice
區別
歡迎關注我的公眾號:程式通事,獲得日常乾貨推送。如果您對我的專題內容感興趣,也可以關注我的部落格:studyidea.cn
從原始碼解析背後的原理
上篇文章中我們看到 ResponseBodyAdvice
的子類使用 @ControllerAdvice
註解,大家有沒有好奇,如果我將@ControllerAdvice
換成 @Controller
註解,還能達到上篇文章的效果嗎?
感興趣的小夥伴可以自己嘗試下,這裡小黑哥自己告訴大家結果了,實際測試結果是不行的。
那為什麼一定要與@ControllerAdvice
搭配才會生效?
首先我們先檢視一下 @ControllerAdvice
的原始碼:
可以看到這個註解上還存在一個我們非常熟悉的 @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
修改返回資訊。
Filter
、Interceptor
、ResponseBodyAdvice
區別
Filter
屬於 Servlet 元件,所有請求將會先進入 Filter
,判斷通過之後才會在進入到真正的具體的請求中。
上圖代表是用 Spring MVC 的一個 Web 專案,所有請求將會先進入到 Filter
,通過之後才會進入到 SpringMVC 中最重要的元件 DispatchServlet
。
而 Interceptor
是 SpringMVC 的元件,它的作用實際上與 Filter
類似, 只不過的它的作用是位於自定義的 Controller
前後。
不管是 Filter
還是 Interceptor
,它們的作用方法域內只能拿到 ServletResponse
的引數,這個時候返回值已經被寫入 ServletResponse
,我們很難再去修改。
而 ResponseBodyAdvice
作用時機位於寫入之前,所以這個時候可以很容易拿到原值進行修改。
總結
SpringMVC 初始化的過程中,將會掃描所有帶有 @ControllerAdvice
註解的類,將其生成為 ControllerAdviceBean
。如果這類剛好為 ResponseBodyAdvice
介面的子類,Spring 將會為其單獨儲存起來,後續將會封裝到的 RequestResponseBodyAdviceChain
,使用責任鏈的模式對請求、響應進行處理。
最後我們解釋了一下 Filter
,Interceptor
,ResponseBodyAdvice
區別,從作用範圍上來講:
Filter>Interceptor>ResponseBodyAdvice
但是前兩者沒辦法修改返回值(時機太晚),只有後者才可以真正在返回值返回之前做到修改。
好了,今天文章就到這裡了,下次我們分享一下如何寫出優雅的 Dubbo 介面,下次見。
歡迎關注我的公眾號:程式通事,獲得日常乾貨推送。如果您對我的專題內容感興趣,也可以關注我的部落格:studyidea.cn