Spring AOP原理分析

AOP是Spring Core中幾大重要能力之一,我們可以使用AOP實現(xiàn)很多功能,比如我們常用的日志處理與Spring中的聲明式事務。

AOP的幾個重要概念

Aspect:切面,在Spring中意為所有通知方法所在的類

Join point:連接點,程序執(zhí)行中的一點,在Spring中只表示方法執(zhí)行(Spring只支持方法級別的攔截)

Advice:通知,在特定連接點上采取的操作,Spring將通知抽象為攔截器,并圍繞連接點維護攔截器鏈。共有5種類型,before(切點之前執(zhí)行),around(環(huán)繞執(zhí)行,即切點前后執(zhí)行),After returning(切點正常執(zhí)行完返回后執(zhí)行),After throwing(切點拋出異常后執(zhí)行),after(切點之后執(zhí)行,不管是異常或正常結束),AOP攔截器鏈則為以上五種通知組成。我們可以在通知方法中獲得我們需要的參數(shù)(返回值,異常信息,代理對象等)

Pointcut:切點,與通知一起出現(xiàn),使用專門的切點表達式?jīng)Q定在何處執(zhí)行通知方法。

Introduction:引入,為類添加新的方法或字段。

Target object:被代理的對象

AOP proxy:AOP代理對象,由JDK動態(tài)代理或CGLIB代理生成

Weaving:織入,將通知等織入代理類。Spring AOP是動態(tài)織入(運行時織入),AspectJ則是靜態(tài)織入(編譯時織入)

幾個注意點

關于Spring AOP的具體使用這里不做介紹,具體見文檔,這里說幾個在使用代理時需要注意的地方。

由于Spring AOP框架基于代理的特性,目標對象內(nèi)的調(diào)用根據(jù)定義不會被攔截。自調(diào)用即類似this.bar()或this.foo()這樣的調(diào)用,即使在bar方法上有通知方法通知也不會執(zhí)行。對于JDK代理,只能攔截代理上的公共接口方法調(diào)用。使用CGLIB,可以攔截代理上的公共和受保護方法調(diào)用(Cglib基于子父類實現(xiàn)代理,而私有方法不會被子類繼承)。當多個通知都想在同一個連接點上運行時,他們將按照優(yōu)先級順序執(zhí)行,優(yōu)先級順序可以使用Order接口來定義。

總結一句:自調(diào)用通知方法不執(zhí)行,私有方法通知不執(zhí)行。

原理

接下來從源碼角度分析下Spring AOP的實現(xiàn)原理。

在Spring中我們使用@EnableAspectJautoProxy開啟AOP功能,我們以此為入口。(其他的Enable注解分析原理都是一樣的,比如EnableAsync等)。

@EnableAspectJautoProxy

它使用@Import注解導入了AspectJAutoProxyRegistrar類,該類實現(xiàn)了ImportBeanDefinitionRegistrar接口,用于向Spring中注冊類。

AspectJAutoProxyRegistrar

在registerBeanDefinitions方法的第一行注冊了AOP需要的相關bean,方法中的下面部分是取EnableAspectJAutoProxy注解的信息,根據(jù)參數(shù)值做相應的處理,這里主要關注方法的首行代碼,進入registerAspectJAnnotationAutoProxyCreatorIfNecessary方法。

發(fā)現(xiàn)最終注冊了AnnotationAwareAspectJAutoProxyCreator。該類是實現(xiàn)AOP的基礎,我們對該類進行分析,首先來看繼承結構

BeanFactoryAware接口主要用于設置BeanFactory,這里我們主要關注InstantiationAwareBeanPostProcessor與BeanPostProcessor接口,實現(xiàn)這兩個接口意味著AnnotationAwareAspectJAutoProxyCreator是一個Spring的后置處理器,后置處理器會在bean的創(chuàng)建過程中起作用,關于后置處理器不熟悉的同學可以去看這篇文章Spring之IOC容器初始化。

InstantiationAwareBeanPostProcessor

InstantiationAwareBeanPostProcessor有一個before與after接口,由接口名可知兩個方法分別在bean實例化前后調(diào)用,關于Spring中bean的實例化過程不清楚的可以看Spring Bean的實例化分析。我們在子類中找到他們的實現(xiàn)(after方法由于沒有特別的處理這里就省略了)

before的實現(xiàn)

首先從緩存中獲取,然后調(diào)用this.isInfrastructureClass(beanClass)判斷創(chuàng)建的類是否為Advice、Pointcut等相關類,若是則放入adviseBean集合并返回null,正常的bean經(jīng)過該方法會返回null,這里主要是用來處理我們的切面類。

isInfrastructureClass

bean創(chuàng)建完成后接下來就是另一個接口BeanPostprocess(實例化,調(diào)用構造函數(shù))開始起作用了。

BeanPostprocess

他會在InstantiationAwareBeanPostProcessor(初始化,即BeanDefination的初始化)接口方法執(zhí)行完之后調(diào)用,查看其實現(xiàn)(before方法由于沒有特別的處理這里就省略了)

最終會調(diào)用wrapIfNecessary方法判斷該bean是否需要增強。進入方法

正常bean的創(chuàng)建會進入到isInfrastructureClass這個分支,isInfrastructureClass這個方法就是之前分析的判斷是否是Aspect等注解的類,如果不是則調(diào)用getAdvicesAndAdvisorsForBean方法獲取到符合該bean的通知方法(即相應的Advisor)。

最終調(diào)用createProxy創(chuàng)建代理對象。

createProxy
接上圖右側

最終進入代理工廠創(chuàng)建代理對象的方法,根據(jù)是否實現(xiàn)接口自動選擇創(chuàng)建JDK動態(tài)代理(基于接口)或者是Cglib代理(基于子父類)。到這里切面以及要被代理的類就都創(chuàng)建完成了,接下來就是如何運行通知方法了。

執(zhí)行流程

我們這里假設上一步創(chuàng)建的對象為Cglib對象,了解過Cglib代理的同學都知道實現(xiàn)代理要實現(xiàn)MethodInterceptor接口,在里面的intercept方法中進行方法的攔截。我們找到代理類的intercept方法

首先調(diào)用getInterceptorsAndDynamicInterceptionAdvice方法獲取所有通知方法的Advisor攔截器鏈,chain不為空會依次調(diào)用對應的Advisor攔截器的proceed方法進行代理調(diào)用,在此會按照通知的順序執(zhí)行原方法與通知方法。

大體的執(zhí)行流程就分析完了,有時間的同學最好簡單寫個demo然后跟著一步步debug,這樣能夠更清晰的了解流程。

最后總結一下,容器初始化時將切面等信息放入通知集合中,正常bean在創(chuàng)建時會判斷該bean是否需要被增強,若需要增強,創(chuàng)建相應的代理對象。在執(zhí)行時,代理對象執(zhí)行相應的invoke方法,在方法中獲取到通知集合并抽象成攔截器鏈,使用攔截器模式按照順序執(zhí)行相應的方法。

附兩張流程圖:

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容