iT邦幫忙

2026 iThome 鐵人賽

DAY 15
0
Software Development

30 篇淺談 Spring 框架的核心底層組件 系列 第 15 篇

Day 15: Advisor —— Pointcut 與 Advice 的切面抽象骨架概述

  • 分享至 

  • xImage
  •  

昨天我們說完了 AOP 代理物件的生成方式,今天我們來簡單講講切面邏輯的創建與調用。在 Spring 框架裡,一個完整的切面邏輯主要需要包含兩個內容,分別是要切在哪裡(由 Pointcut 負責定義)、還有切了之後要做什麼(由 Advice 負責定義)。

這裡先從 Advice 開始:Spring 的 Advice 全名是 org aopalliance aop Advice,它不是 Spring AOP 自定義介面,是比 Spring AOP 上層的 AOP Alliance 規範。Advice 底下繼承(或實作)的類別非常多,但如果以我們日常開發的角度來看,可以聚焦在 Abstract-AspectJ-Advice 就好。Abstract-AspectJ-Advice 是 Spring AOP 自定義的抽象類別,負責處理 AspectJ 註解的底層邏輯,它底下有 5 個 sub-Advices,分別對應日常開發中的 @Before, @After, ...等五個切面註解。

在實際的運行上,無論是 CGLIB 亦或是 JDK 動態代理,它們都不會直接調用 Advice 介面的相關方法,進行 AOP 的邏輯擴充。Spring 為了架構設計與規範統一,在代理物件的責任鏈上,Spring 會使用 Method-Invocation 的 proceed() 方法進行目標物件的方法執行。

Method-Invocation 本身是一個介面,Spring 底層的實作類別同時持有目標物件和責任鏈上攔截器。這裡的攔截器指的是 Method-Interceptors,也就是 sub-Advice 跟 CGLIB 代理(或 JDK 動態代理)真正接合的地方:部分的 sub-Advices(例如 AspectJAfterAdvice)因為本身就有實作 Method-Interceptor 介面,所以可以直接實作介面裡面的 invoke 方法,完成代理前後的相關邏輯,至於沒有實作介面的 sub-Advices(例如 AspectJMethodBeforeAdvice),Spring 則設計了對應的 adapter,用來適配切面邏輯跟 AOP 框架的具體接合。

粗略地講完 Advice 之後,再來我們來看 Pointcut:Pointcut 本身是個介面,內部規範了 2 個方法,getClassFilter 用來取得類別過濾器,這個過濾器可以決定指定的目標類別會不會被這個 Pointcut 切到,實際用法有點像是 Servlet 裡面的 Filter 組件,getMethodMatcher 則用來取得方法過濾器,負責判斷通過類別篩檢的目標方法是不是這個 Pointcut 的作用對象。

只有當 ClassFilter 與 MethodMatcher 同時判定為 true 時,該方法才會被判定需要進行 AOP 增強。進而觸發後續的 Advice 相關邏輯。我們常用的 @Pointcut 註解,底層就是由實作 Pointcut 介面的 AspectJ-Expression-Pointcut 負責處理的,透過整合 AspectJ 原生的語法解析引擎,將 execution(...) 或 @annotation(...) 字串解析成底層的比對規則,並對目標方法進行高細粒度的精確比對。

接下來是 Advisor:Advisor 本身是個介面,同樣定義在 Spring AOP 的模組裡面。介面內部定義了 2 個方法,分別是 getAdvice(用來取得最上面說的 Advice)和 isPerInstance(歷史包袱,暫時忽略)。Advisor 底下最常提到的子介面是 Pointcut-Advisor,內部多定義了一個 getPointcut 方法取得上一段的 Pointcut 物件。

換個角度來看,Advisor 其實就是把「切在哪裡」跟「切了要做什麼」這兩件事,包裝成一個可以被 IoC 容器統一管理、統一排序的最小單位。此外,Advisor 同樣允許開發者進行排序,類似於 BPP 或 BFPP 的邏輯,我們同樣可以搭配 Ordered / @Order 註解來決

最後,我們簡單歸納一下三者之間的關係與對 Bean 的作用:當 IoC 容器在進行 refresh 時,會在 Bean 的實例化階段,將容器中所有實作 Advisor 介面的 Bean 蒐集起來,並交由一個負責自動代理生成的 BPP 統一管理,後續當 Bean 工廠逐一將 BD 轉變為 Bean 物件時,在初始化完成之後的後置處理階段(對應 doCreateBean 的 Step. 9),就會調用這個 BPP,逐一判斷 Advisor 裡面的 Pointcut 是否可以通過類別篩檢跟方法篩檢,並將原始 Bean 包覆成帶有 Advice 邏輯的代理物件。

如此一來,Pointcut、Advice、Advisor、和 Bean 這四者便串聯成一條完整的 AOP 邏輯鏈:Pointcut 決定切點、Advice 定義切面邏輯、Advisor 則將兩者封裝並交由容器統一調度,最終讓我們只需透過註解(或 XML)設定切面,達成不修改原始程式碼的前提下,動態為目標 Bean 織入橫切關注點。


期待後續各位的閱讀與分享,我是 Pax,我們明天見。


上一篇
Day 14: AopProxy —— 動態代理的底層實現與調用分發機制
下一篇
Day 16: AbstractAutoProxyCreator - 將 Bean 封裝為切面代理對象的自動化引擎
系列文
30 篇淺談 Spring 框架的核心底層組件 共 17 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言