Spring源碼分析總結——AOP

該文章基於《Spring源碼深度解析》撰寫,感謝郝佳老師的奉獻

AOP的實現原碼

開啓AOP需要在配置文件中聲明<aop:aspectj-autoproxy>,針對聲明我們進入AspectJAutoproxyBeanDefinitionParser類
由於所有的解析器都實現了BeanDefinitionParser接口,所以其入口都爲Parse方法,AspeJAutoProxyBeanDefinitionParser也不例外,其入口如下:

public BeanDefinition parse(Element element, ParserContext parserContext) {
    AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
    this.extendBeanDefinition(element, parserContext);
    return null;
}

很明顯其中的registerAspectJAnnotationAutoProxyCreatorIfNecessary就是我們要深入研究的重點。
在registerAspectJAnnotationAutoProxyCreatorIfNecessary中主要完成了2個任務:

1.註冊或者增強AnnotationAwareAspectJAutoProxyCreator,AnnotationAwareAspectJAutoProxyCreator實現了基本的AOP效果,所以第一步通過優先級判斷哪個自動代理創建器最重要,然後通過改變對應的beanName進行註冊和增強。

2.處理proxy-target-class以及expose-proxy屬性。
proxy-target-class有兩種實現方式:
I.JDK動態代理由於是通過在運行期間創建一個接口的實現類來完成代理所以至少實現一個接口。
II.CGLIB代理由於其操作字節碼的特性(三個問題,無法通知final方法,需要引入額外包至classpath下,額外的配置)
expose-proxy存在的問題是對象內部的自我調用將無法實施切面中的增強。通過將this.method()修改爲((Aservice)AopContext.currentProxy()).method()可以避免這個問題。

創建AOP代理

接下來就是創建代理的問題了,但是AspeJAutoProxyBeanDefinitionParser並不負責創建代理,創建代理是由其父類AbstractAutoProxyCreator的postProcessAfterInitialization方法完成的,正好我們可以觀察他的類層次結構:
這裏寫圖片描述
postProcessAfterInitialization完成代理主要包含兩個步驟:獲取增強方法或者增強器——>然後根據獲取的增強進行代理,下面對這兩個方法(兩個方法都由AbstractAutoProxyCreator的getAdvicesAndAdvisorsForBean方法完成)進行分解:

獲取增強器

該步可分爲兩步,第一步是獲取全部增強器,第二步是進行增強器的篩選
獲取全部增強器
增強器的獲取又要回到AspeJAutoProxyBeanDefinitionParser這個子類中,其實他的過程無非是:
(1)獲取所有bean
(2)找出所有帶有@AspectJ註解的類
(3)對所有帶有@AspectJ註解的類進行增強器的提取
(4)將提取結果加入緩存
第(3)步中增強器的獲取並不是那麼簡單,它包括了三個步驟:

第一步:普通增強器的獲取,該步驟又可細分爲兩步:1.切點信息的獲取(由AspectJAdvisorFactory的getAdvisor方法完成)2.根據切點信息生成增強器(由InstantiationModelAwarePointcutAdvisorImpl進行統一封裝)——Spring會根據不同的註解類型生成不同的增強器
對於前置增強器與後置增強器Spring處理的方式並不相同,前置增強主要是通過在攔截鏈中放置MethodBeforeAdviceInterceptor,然後在MethodBeforeAdvice中又放置了AspectJMethodBeforeAdvice,通過串聯調用完成調用;後置增強通過直接在攔截鏈中使用AspectJAfterAdvice。

第二步:增加同步實例化增強器,該步驟的前提是尋找的增強器不爲空並且又配置了增強延遲初始化

第三步:獲取DeclareParents註解,DeclareParents主要用於引介增強的註解形式的實現,實現方式與普通增強非常相似,只不過使用DeclareParentsAdvisor進行封裝。

尋找匹配的增強器
這一步不太複雜,大致就是將引介增強與普通增強分開進行處理

創建代理

對於代理類的創建Spring都交給了ProxyFactory去處理,初始化操作步驟基本上包括了下面的流程:
(1)獲取當前類中的屬性
(2)添加代理接口
(3)封裝Advisor並添加至ProxyFactory中

這一步比較繁瑣,所以多說一些,因爲Spring中有數量衆多的攔截器與增強器,增強方法等等,所以在封裝時都需要進一步封裝爲Advisor進行代理的創建

(4)設置需要代理的類
(5)自定義customizeProxyFactory函數
(6)進行封裝獲取代理
Spring中通過判斷(是否採用激進的優化 || 是否本身被代理而不是目標類接口 || 是否存在代理接口 )並且要求目標類不是接口的情況下可以採用CGLIB的方法實現AOP,否則使用JDK動態代理
其中JDK動態代理與CGLIB字節碼生成的區別在於:
JDK動態代理只能對實現了接口的類生成代理,而不能針對類
CGLIB主要是針對類實現代理,主要是針對指定的類生成子類,覆蓋其中的方法,所以最好不要使用final

下面來講講JDK和CGLIB是具體如何實現代理的:
JDK實現代理:JDK實現代理首先需要實現IvocationHandler接口並實現其中的invoke和getproxy方法,並且將需要代理的對象通過構造函數傳入。實際在Spring中通過JdkDynamicAopProxy實現IvocationHandler接口來完成,在JdkDynamicAopProxy的invoke方法中通過ReflectiveMethodInvocation類進行了調用鏈的封裝,並且由該類的proceed方法實現了攔截器的逐一調用,但是實際上的該類只是維護了鏈接調用的計數器,增強的順序由增強器內部進行邏輯實現。

CGLIB實現代理:CGLIB實現代理需要一個實現了MethodInterceptor接口的類作爲增強器,並通過Ehancer類的setCallbacks將MethodInterceptor接口的實現類作爲增強器進行注入,Cglib2AopProxy類的getProxy完成了這一目的,並且封裝了攔截器爲DynamicAdvisedInterceptor類,在該類中又通過構造調用鏈的(CglibMethodInvocation(ReflectiveMethodInvocation的子類)proceed方法進行調用

靜態AOP

原理
既然存在動態代理,那在Spring中也應該存在靜態代理,由於靜態代理在啓動時便完成了對字節碼的增強,所以效率上會高一些,Spring中的靜態AOP直接使用了AspectJ提供的方法,而AspectJ又是在Instrument(java.lang.Instrument)基礎上進行的封裝,其中最重要的就是ClassFIleTransformer接口的實現類。通過Instumentation類的addTransformer方法添加ClassFIleTransformer接口的實現類就可以達到靜態AOP的效果

實際上Spring框架在實現靜態AOP的時候通過了下面兩個步驟
(1)解析自定義標籤(<context:load-time-weaver>)
首先查詢是否開啓AspectJ(<context:load-time-weaver>),同時檢測是否存在META-INF/aop.xml文件,如果開啓了AspectJ,那麼接下來會註冊
1.AspectJWeavingEnabler(封裝在BeanDefinition),
2.DefaultContextLoadTimeWeaver(由<context:load-time-weaver>產生),
3.LoadTimeWeaverAwareProcessor(容器初始化時添加,激活AspectJ的前提)

(2)織入
首先來看一下接下來用到的幾個類型的繼承關係
繼承關係
先解釋一下其中出現的幾個接口
1.Ordered接口保證在實例化bean時當前的bean會最先被初始化
2.BeanClassLoaderAware保證了在bean初始化時將會調用AbstractAutoWireCapableBeanFactory的invokeMethods時將beanClassLoader賦值給當前類
3.DisposableBean銷燬bean時調用destroy方法
最後由於AspectJWeavingEnabler實現了BeanFactoryPostProcessor,所以當所有bean解析結束後會調用postProcessBeanFactory來進行織入

發佈了52 篇原創文章 · 獲贊 8 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章