【Spring專場】「AOP容器」不看原始碼就帶你認識核心流程以及運作原理

浩宇天尚發表於2022-01-09

前提回顧

前一篇文章主要介紹了spring核心特性機制的IOC容器機制和核心運作原理,接下來我們去介紹另外一個較為核心的功能,那就是AOP容器機制,主要負責承接前一篇代理模式機制中動態代理:JDKProxy和CglibProxy的功能機制之後,我們開始研究一下如何實現一下相關的AOP容器代理機制的。

AOP入口機制

如何實現將Aspectj的動態weave織入到Spring容器的Bean中?

實現的基本實現原理就是後置處理器:BeanPostProcessor機制,實現動態化植入機制。

如何實現相關的Aspectj的weave織入時機

bean在初始化的時候會進行呼叫對應的BeanPostProcessor的對應的方法會進行織入。

判斷的基本流程

主要取決於wrapIfNecessary方法:

判斷當前的Bean是AOP的基礎設施型別

如果是基礎設施型別,則直接回進行返回該bean物件,不會進行相關的初始化對應的aspectj的動態織入機制。

如果屬於定製化的bean物件型別

會進行尋找相關的Bean對應的何時的加強通知類。

如果對應該物件的通知增強陣列集合不為空

則會對該bean物件,額外進行增強操作生成相關的代理物件,並返回該執行之後的物件,否則會直接返回該物件即可。

篩選何時的通知器

getAdvicesAndAdvisorsForBean方法是我們篩選Advice增強類的核心方法,主要用於過濾和篩選對應該bean的何時的增強器陣列資訊。

查詢對應Bean的通知增強器

主要用於呼叫AnnotationAwareAspectJAutoProxyCreatorfindCandidateAdvisors()方法,其內部會進行先關的核心構建相關的Aspectj的類的相關實現操作

構建先關Aspectj類buildAspectJAdvisors方法

  1. 首先先進行獲取先關的所有容器的BeanName資料集合
  2. 在根絕上面的BeanName名稱集合獲取相關的BeanType型別集合
  3. 根據BeanType判斷當前的Bean是否屬於一個Aspectj的註解.類,如果不是則類不做任何處理。

構建實際的相關的Advisors類機制

advisorsFactory.getAdvisors獲取通知器

切點處理

切點類處理操作到此為止,還不完整接下來才是構建動態代理物件的真正執行操作,

  1. 獲取上面操作中獲取到的Aspectj類中的除了PointCut註解修飾的其他的方法列表。
  2. 根據對應的Aspectj類和相關的Advisors方法列表,建立相關的Advisor實現類,其中內部會進行遍歷相關上一步的方法列表,並且呼叫對應的方法method對應的getAdvisor方法,建立Advisor物件。
  3. 建立相關的AspectJExpressionPointCut物件,並且從方法裡的註解表示式進行解析,這最後設定到對應的門面層的Advisor物件例項。
  4. 實際的Advisor物件實現類物件的實際:InstantitationModelAwarePoincutAdvisorImpl例項,並且呼叫其內部的instantiateAdvice方法構建通知機制。
  5. 其內部仍然會呼叫getAdvice方法,並且構建相關的註解的型別建立相應的通知。

篩選何時的通知器並且執行應用

findAdvisorsThatCanApply方法

擴充套件相關的篩選出的通知器列表,extendAdvisors方法,通知器列表首部新增一個DefaultPointcutAdivosr型別的通知器,也就是ExposeInvocationInterceptor.ADVISOR的實現機制。

建立代理物件

  • jdk動態代理
  • cglib動態代理
proxy-target-class

proxy-target-class的屬性值,代表是否可以支援代理實現類,預設採用的false代表著,當bean有實現介面的時候,會直接採用jdk的動態代理機制生成代理物件,如果是true,則代表著使用cglib進行生成代理物件。

例如:
<aop:aspectj-autoproxy proxy-target-class = "true" /></aop>
AopProxy介面
  • CglibAopProxy介面實現
  • JdkDynamicAopProxy介面實現

AOP代理物件呼叫同類的方法問題解決方案

expose-proxy作用

前提是必須要配置相關的expose-proxy屬性配置值為true,才會進行暴露對應的代理機制。

為了解決目標方法呼叫同物件中的其他方法,其他方法的切面邏輯是無法實現,因為會涉及到相關的this操作而不是proxy物件機制。

可以實現使用AopContext.currentProxy()強制轉換為當前的代理物件。

攔截器鏈路執行

intercept方法機制

獲取相關的對應方法的攔截器棧鏈路,如果沒有獲取到相關的快取鏈路,則會直接呼叫相關的getInterceptorsAndDynamicInterceptorAdvice獲取先關的攔截器鏈。

方法攔截器相關的攔截操作連線點

會進行先關的PointcutAdvisor型別通知器,這裡會呼叫相關的通知器所持有的切點(Pointcut)對類和方法進行匹配,匹配衝過這說明相關的向當前的方法進行織入邏輯控制。此外還會通過geIntercptors()方法對非MethodIntercptor型別的通知進行轉換。返回相關的攔截器陣列,並且隨後存入快取中。

執行目標方法的方式

如果攔截器為空

則會直接通過代理機制的反射控制進行呼叫執行即可。

如果不為空

則例如jdkDynamicAutoProxy物件進行呼叫構建ReflectiveMethodInvocation物件,例如它的process方法啟動攔截器棧的invoke方法。

  • invoke:執行攔截器棧
  • invokeJoinpoin():執行目標方法

處理返回值,並且返回該值。

相關文章