關于Spring AOP,除了動態(tài)代理、CGLIB,你還知道什么?
Spring 作為 Java 中最流行的框架,主要歸功于其提供的 IOC 和 AOP 功能。本文將討論 Spring AOP 的實現(xiàn)。第一節(jié)將介紹 AOP 的相關概念,若熟悉可跳過,第二節(jié)中結合源碼介紹 Spring 是如何實現(xiàn) AOP 的各概念。
1. AOP 概念
1.1 JoinPoint
進行織入操作的程序執(zhí)行點。
常見類型:
方法調(diào)用(Method Call):某個方法被調(diào)用的時點。
方法調(diào)用執(zhí)行(Method Call Execution):某個方法內(nèi)部開始執(zhí)行的時點。
方法調(diào)用是在調(diào)用對象上的執(zhí)行點,方法調(diào)用執(zhí)行是在被調(diào)用對象的方法開始執(zhí)行點。
構造方法調(diào)用(Constructor Call):對某個對象調(diào)用其構造方法的時點。
構造方法執(zhí)行(Constructor Call Execution):某個對象構造方法內(nèi)部開始執(zhí)行的時點。
字段設置(Field Set):某個字段通過 setter 方法被設置或直接被設置的時點。
字段獲取(Field Get):某個字段通過 getter 方法被訪問或直接被訪問的時點。
異常處理執(zhí)行(Exception Handler Execution):某些類型異常拋出后,異常處理邏輯執(zhí)行的時點。
類初始化(Class Initialization):類中某些靜態(tài)類型或靜態(tài)塊的初始化時點。
1.2 Pointcut
Jointpoint 的表述方式。
常見表述方式:
直接指定 Joinpoint 所在方法名稱 正則表達式 特定的 Pointcut 表述語言
1.3 Advice
單一橫切關注點邏輯的載體,織入到 Joinpoint 的橫切邏輯。
具體形式:
Before Advice:Joinpoint 處之前執(zhí)行。 After Advice:Joinpoint 處之后執(zhí)行,細分為三種: After Returning Advice:Joinpoint 處正常完成后執(zhí)行。 After Throwing Advice:Joinpoint 處拋出異常后執(zhí)行。 After Finally Advice:Joinpoint 處正常完成或拋出異常后執(zhí)行。 Around Advice:包裹 Joinpoint,在 Joinpoint 之前和之后執(zhí)行,具有 Before Advice 和 After Advice 的功能。 Introduction:為原有的對象添加新的屬性或行為。
1.4 Aspect
對橫切關注點邏輯進行模塊化封裝的 AOP 概念實體,包含多個 Pointcut 和相關 Advice 的定義。
1.5 織入和織入器
織入:將 Aspect 模塊化的橫切關注點集成到 OOP 系統(tǒng)中。
織入器:用于完成織入操作。
1.6 Target
在織入過程中被織入橫切邏輯的對象。
將上述 6 個概念放在一塊,如下圖所示:

在了解 AOP 的各種概念后,下面將介紹 Spring 中 AOP 概念的具體實現(xiàn)。
2. Spring 中的實現(xiàn)
前文提到 AOP 的 Joinpoint 有多種類型,方法調(diào)用、方法執(zhí)行、字段設置、字段獲取等。而在 Spring AOP 中,僅支持方法執(zhí)行類型的 Joinpoint,但這樣已經(jīng)能滿足 80% 的開發(fā)需要,如果有特殊需求,可求助其他 AOP 產(chǎn)品,如 AspectJ。由于 Joinpoint 涉及運行時的過程,相當于組裝好所有部件讓 AOP 跑起來的最后一步。所以將介紹完其他概念實現(xiàn)后,最后介紹 Joinpoint 的實現(xiàn)。
2.1 Pointcut
由于 Spring AOP 僅支持方法執(zhí)行類別的 Joinpoint,因此 Pointcut 需要定義被織入的方法,又因為 Java 中方法封裝在類中,所以 Pointcut 需要定義被織入的類和方法,下面看其實現(xiàn)。
Spring 用 org.springframework.aop.Pointcut 接口定義 Pointcut 的頂層抽象。
public?interface?Pointcut?{
????
???//?ClassFilter用于匹配被織入的類???
???ClassFilter?getClassFilter();
???//?MethodMatcher用于匹配被織入的方法
???MethodMatcher?getMethodMatcher();
???//?TruePoincut的單例對象,默認匹配所有類和方法
???Pointcut?TRUE?=?TruePointcut.INSTANCE;
}
我們可以看出,Pointcut 通過 ClassFilter 和 MethodMatcher 的組合來定義相應的 Joinpoint。Pointcut 將類和方法拆開來定義,是為了能夠重用。例如有兩個 Joinpoint,分別是 A 類的 fun() 方法和 B 類的 fun() 方法,兩個方法簽名相同,則只需一個 fun() 方法的 MethodMatcher 對象,達到了重用的目的,ClassFilter 同理。
下面了解下 ClassFilter 和 MethodMatcher如何進行匹配。
ClassFilter 使用matches方法匹配被織入的類,定義如下:
public?interface?ClassFilter?{
????
?//?匹配被織入的類,匹配成功返回true,失敗返回false
?boolean?matches(Class>?clazz);
?//?TrueClassFilter的單例對象,默認匹配所有類
?ClassFilter?TRUE?=?TrueClassFilter.INSTANCE;
}
MethodMatcher 也是使用 matches方法 匹配被織入的方法,定義如下:
public?interface?MethodMatcher?{
????
???//?匹配被織入的方法,匹配成功返回true,失敗返回false
???//?不考慮具體方法參數(shù)
???boolean?matches(Method?method,?Class>?targetClass);
????
???//?匹配被織入的方法,匹配成功返回true,失敗返回false
???//?考慮具體方法參數(shù),對參數(shù)進行匹配檢查
???boolean?matches(Method?method,?Class>?targetClass,?Object...?args);
???
???//?一個標志方法
???//?false表示不考慮參數(shù),使用第一個matches方法匹配
???//?true表示考慮參數(shù),使用第二個matches方法匹配
???boolean?isRuntime();
???//?TrueMethodMatcher的單例對象,默認匹配所有方法
???MethodMatcher?TRUE?=?TrueMethodMatcher.INSTANCE;
}
看到 matches 方法的聲明,你是否會覺得有點奇怪,在 ClassFilter 中不是已經(jīng)對類進行匹配了嗎,那為什么在 MethodMatcher 的 matches 方法中還有一個 Class> targetClass 參數(shù)。請注意,這里的 Class> 類型參數(shù)將不會進行匹配,而僅是為了找到具體的方法。例如:
public?boolean?matches(Method?method,?Class>?targetClass)?{
????
????Method?targetMethod?=?AopUtils.getMostSpecificMethod(method,?targetClass);
????// ...
}
在MethodMatcher相比ClassFilter特殊在有兩個 matches 方法。將根據(jù) isRuntime() 的返回結果決定調(diào)用哪個。而MethodMatcher因isRuntime()分為兩個抽象類 StaticMethodMatcher(返回false,不考慮參數(shù))和 DynamicMethodMatcher(返回true,考慮參數(shù))。
Pointcut 也因 MethodMathcer 可分為 StaticMethodMatcherPointcut 和 DynamicMethodMatcherPointcut,相關類圖如下所示:

DynamicMethodMatcherPointcut 本文將不介紹,主要介紹下類圖中列出的三個實現(xiàn)類。
(1)NameMatchMethodPointcut
通過指定方法名稱,然后與方法的名稱直接進行匹配,還支持 “*” 通配符。
public?class?NameMatchMethodPointcut?extends?StaticMethodMatcherPointcut?implements?Serializable?{
????
?//?方法名稱
?private?List?mappedNames?=?new?ArrayList<>();
?//?設置方法名稱
?public?void?setMappedNames(String...?mappedNames)?{
??this.mappedNames?=?new?ArrayList<>(Arrays.asList(mappedNames));
?}
?@Override
?public?boolean?matches(Method?method,?Class>?targetClass)?{
??for?(String?mappedName?:?this.mappedNames)?{
???//?根據(jù)方法名匹配,isMatch提供“*”通配符支持
???if?(mappedName.equals(method.getName())?||?isMatch(method.getName(),?mappedName))?{
????return?true;
???}
??}
??return?false;
?}
????
?//?...
}
(2)JdkRegexpMethodPointcut
內(nèi)部有一個 Pattern 數(shù)組,通過指定正則表達式,然后和方法名稱進行匹配。
(3)AnnotationMatchingPointcut
根據(jù)目標對象是否存在指定類型的注解進行匹配。
2.2 Advice
Advice 為橫切邏輯的載體,Spring AOP 中關于 Advice 的接口類圖如下所示:

(1)MethodBeforeAdvice
橫切邏輯將在 Joinpoint 方法之前執(zhí)行。可用于進行資源初始化或準備性工作。
public?interface?MethodBeforeAdvice?extends?BeforeAdvice?{
????
?void?before(Method?method,?Object[]?args,?@Nullable?Object?target)?throws?Throwable;
????
}
下面來實現(xiàn)一個 MethodBeforeAdvice,看下其效果。
public?class?PrepareResourceBeforeAdvice?implements?MethodBeforeAdvice?{
????
?@Override
?public?void?before(Method?method,?Object[]?args,?Object?target)?throws?Throwable?{
??System.out.println("準備資源");
?}
????
}
定義一個 ITask 接口:
public?interface?ITask?{
????
?void?execute();
????
}
ITask 的實現(xiàn)類 MockTask:
public?class?MockTask?implements?ITask?{
????
???@Override
???public?void?execute()?{
??????System.out.println("開始執(zhí)行任務");
??????System.out.println("任務完成");
???}
????
}
Main 方法如下,ProxyFactory、Advisor 在后續(xù)會進行介紹,先簡單了解下,通過ProxyFactory拿到代理類,Advisor用于封裝 Pointcut 和 Advice。
public?class?Main?{
????
???public?static?void?main(String[]?args)?{
??????MockTask?task?=?new?MockTask();
??????ProxyFactory?weaver?=?new?ProxyFactory(task);
??????weaver.setInterfaces(new?Class[]{ITask.class});
??????//?內(nèi)含一個NameMatchMethodPointcut
??????NameMatchMethodPointcutAdvisor?advisor?=?new?NameMatchMethodPointcutAdvisor();
??????//?指定NameMatchMethodPointcut的方法名
??????advisor.setMappedName("execute");
??????//?指定Advice
??????advisor.setAdvice(new?PrepareResourceBeforeAdvice());
??????weaver.addAdvisor(advisor);
??????ITask?proxyObject?=?(ITask)?weaver.getProxy();
??????proxyObject.execute();
???}
????
}
/**?output:
準備資源
開始執(zhí)行任務
任務完成
**/
可以看出在執(zhí)行代理對象 proxyObject 的 execute 方法時,先執(zhí)行了 PrepareResourceBeforeAdvice 中的 before 方法。
(2)ThrowsAdvice
橫切邏輯將在 Joinpoint 方法拋出異常時執(zhí)行。可用于進行異常監(jiān)控工作。
ThrowsAdvice 接口未定義任何方法,但約定在實現(xiàn)該接口時,定義的方法需符合如下規(guī)則:
void?afterThrowing([Method,?args,?target],?ThrowableSubclass)
前三個參數(shù)為 Joinpoint 的相關信息,可省略。ThrowableSubclass 指定需要攔截的異常類型。
例如可定義多個 afterThrowing 方法捕獲異常:
public?class?ExceptionMonitorThrowsAdvice?implements?ThrowsAdvice?{
????
?public?void?afterThrowing(Throwable?t)?{
??System.out.println("發(fā)生【普通異常】");
?}
?public?void?afterThrowing(RuntimeException?e)?{
??System.out.println("發(fā)生【運行時異常】");
?}
?public?void?afterThrowing(Method?m,?Object[]?args,?Object?target,?ApplicationException?e)?{
??System.out.println(target.getClass()?+?m.getName()?+?"發(fā)生【應用異常】");
?}
????
}
修改下 MockTask 的內(nèi)容:
public?class?MockTask?implements?ITask?{
????
?@Override
?public?void?execute()?{
??System.out.println("開始執(zhí)行任務");
??//?拋出一個自定義的應用異常
??throw?new?ApplicationException();
//?System.out.println("任務完成");
?}
????
}
修改下 Main 的內(nèi)容:
public?class?Main?{
????
?public?static?void?main(String[]?args)?{
??MockTask?task?=?new?MockTask();
??ProxyFactory?weaver?=?new?ProxyFactory(task);
??weaver.setInterfaces(new?Class[]{ITask.class});
??NameMatchMethodPointcutAdvisor?advisor?=?new?NameMatchMethodPointcutAdvisor();
??advisor.setMappedName("execute");
??//?指定異常監(jiān)控Advice
??advisor.setAdvice(new?ExceptionMonitorThrowsAdvice());
??weaver.addAdvisor(advisor);
??ITask?proxyObject?=?(ITask)?weaver.getProxy();
??proxyObject.execute();
?}
????
}
/**?output:
開始執(zhí)行任務
class?com.chaycao.spring.aop.MockTaskexecute發(fā)生【應用異常】
**/
當拋出 ApplicationException 時,被相應的 afterThrowing 方法捕獲到。
(3)AfterReturningAdvice
橫切邏輯將在 Joinpoint 方法正常返回時執(zhí)行。可用于處理資源清理工作。
public?interface?AfterReturningAdvice?extends?AfterAdvice?{
????
???void?afterReturning(@Nullable?Object?returnValue,?Method?method,?Object[]?args,?@Nullable?Object?target)?throws?Throwable;
????
}
實現(xiàn)一個資源清理的 Advice :
public?class?ResourceCleanAfterReturningAdvice?implements?AfterReturningAdvice?{
????
???@Override
???public?void?afterReturning(Object?returnValue,?Method?method,?Object[]?args,?Object?target)?throws?Throwable?{
??????System.out.println("資源清理");
???}
????
}
修改 MockTask 為正常執(zhí)行成功, 修改 Main 方法為指定 ResourceCLeanAfterReturningAdvice,效果如下:
/**?output:
開始執(zhí)行任務
任務完成
資源清理
**/
(4)MethodInterceptor
相當于 Around Advice,功能十分強大,可在 Joinpoint 方法前后執(zhí)行,甚至修改返回值。其定義如下:
public?interface?MethodInterceptor?extends?Interceptor?{
????
?Object?invoke(MethodInvocation?invocation)?throws?Throwable;
????
}
MethodInvocation 是對 Method 的封裝,通過 proceed() 對方法進行調(diào)用。下面舉個例子:
public?class?AroundMethodInterceptor?implements?MethodInterceptor?{
???@Override
???public?Object?invoke(MethodInvocation?invocation)?throws?Throwable?{
??????System.out.println("準備資源");
??????try?{
?????????return?invocation.proceed();
??????}?catch?(Exception?e)?{
?????????System.out.println("監(jiān)控異常");
?????????return?null;
??????}?finally?{
?????????System.out.println("資源清理");
??????}
???}
???
}
上面實現(xiàn)的 invoke 方法,一下子把前面說的三種功能都實現(xiàn)了。
以上 4 種 Advice 會在目標對象類的所有實例上生效,被稱為 per-class 類型的 Advice。還有一種 per-instance 類型的 Advice,可為實例添加新的屬性或行為,也就是第一節(jié)提到的 Introduction。
(5)Introduction
Spring 為目標對象添加新的屬性或行為,需要聲明接口和其實現(xiàn)類,然后通過攔截器將接口的定義和實現(xiàn)類的實現(xiàn)織入到目標對象中。我們認識下 DelegatingIntroductionInterceptor,其作為攔截器,當調(diào)用新行為時,會委派(delegate)給實現(xiàn)類來完成。
例如,想在原 MockTask 上進行加強,但不修改類的聲明,可聲明一個新的接口 IReinfore:
public?interface?IReinforce?{
???String?name?=?"增強器";
???void?fun();
}
再聲明一個接口的實現(xiàn)類:
public?class?ReinforeImpl?implements?IReinforce?{
?@Override
?public?void?fun()?{
??System.out.println("我變強了,能執(zhí)行fun方法了");
?}
}
修改下 Main 方法:
public?class?Main?{
???
???public?static?void?main(String[]?args)?{
??????MockTask?task?=?new?MockTask();
??????ProxyFactory?weaver?=?new?ProxyFactory(task);
??????weaver.setInterfaces(new?Class[]{ITask.class});
??????//?為攔截器指定需要委托的實現(xiàn)類的實例
??????DelegatingIntroductionInterceptor?delegatingIntroductionInterceptor?=
????????????new?DelegatingIntroductionInterceptor(new?ReinforeImpl());
??????weaver.addAdvice(delegatingIntroductionInterceptor);
??????ITask?proxyObject?=?(ITask)?weaver.getProxy();
??????proxyObject.execute();
??????//?使用IReinfore接口調(diào)用新的屬性和行為
??????IReinforce?reinforeProxyObject?=?(IReinforce)?weaver.getProxy();
??????System.out.println("通過使用"?+?reinforeProxyObject.name);
??????reinforeProxyObject.fun();
???}
???
}
/**?output:
開始執(zhí)行任務
任務完成
通過使用增強器
我變強了,能執(zhí)行fun方法了
**/
代理對象 proxyObject 便通過攔截器,可以使用 ReinforeImpl 實現(xiàn)類的方法。
2.3 Aspect
Spring 中用 Advisor 表示 Aspect,不同之處在于 Advisor 通常只持有一個 Pointcut 和一個 Advice。Advisor 根據(jù) Advice 分為 PointcutAdvisor 和 IntroductionAdvisor。
2.3.1 PointcutAdvisor
常用的 PointcutAdvisor 實現(xiàn)類有:
(1) DefaultPointcutAdvisor
最通用的實現(xiàn)類,可以指定任意類型的 Pointcut 和除了 Introduction 外的任意類型 Advice。
Pointcut?pointcut?=?...;?//?任意類型的Pointcut
Advice?advice?=?...;?//?除了Introduction外的任意類型Advice
DefaultPointcutAdvisor?advisor?=?new?DefaultPointcutAdvisor();
advisor.setPointcut(pointcut);
advisor.setAdvice(advice);
(2)NameMatchMethodPointcutAdvisor
在演示 Advice 的代碼中,已經(jīng)有簡單介紹過,內(nèi)部有一個 NameMatchMethodPointcut 的實例,可持有除 Introduction 外的任意類型 Advice。
Advice?advice?=?...;?//?除了Introduction外的任意類型Advice
NameMatchMethodPointcutAdvisor?advisor?=?new?NameMatchMethodPointcutAdvisor();
advisor.setMappedName("execute");
advisor.setAdvice(advice);
(3)RegexpMethodPointcutAdvisor
內(nèi)部有一個 RegexpMethodPointcut 的實例。
2.3.2 IntroductionAdvisor
只能支持類級別的攔截,和 Introduction 類型的 Advice。實現(xiàn)類有 DefaultIntroductionAdvisor。
DelegatingIntroductionInterceptor?introductionInterceptor?=
????new?DelegatingIntroductionInterceptor(new?ReinforeImpl());
??DefaultIntroductionAdvisor?advisor?=?new?DefaultIntroductionAdvisor(introductionInterceptor,?IReinforce.class);
2.4 織入和織入器
在演示 Advice 的代碼中,我們使用 ProxyFactory 作為織入器
MockTask?task?=?new?MockTask();
//?織入器
ProxyFactory?weaver?=?new?ProxyFactory(task);
weaver.setInterfaces(new?Class[]{ITask.class});
NameMatchMethodPointcutAdvisor?advisor?=?new?NameMatchMethodPointcutAdvisor();
advisor.setMappedName("execute");
advisor.setAdvice(new?PrepareResourceBeforeAdvice());
weaver.addAdvisor(advisor);
//?織入,返回代理對象
ITask?proxyObject?=?(ITask)?weaver.getProxy();
proxyObject.execute();
ProxyFactory 生成代理對象方式有:
如果目標類實現(xiàn)了某些接口,默認通過動態(tài)代理生成。 如果目標類沒有實現(xiàn)接口,默認通過CGLIB生成。 也可以直接設置 ProxyFactory生成的方式,即使實現(xiàn)了接口,也能使用CGLIB。
在之前的演示代碼中,我們沒有啟動 Spring 容器,也就是沒有使用 Spring IOC 功能,而是獨立使用了 Spring AOP。那么 Spring AOP 是如何與 Spring IOC 進行整合的?是采用了 Spring 整合最常用的方法 —— FactoryBean。
ProxyFactoryBean 繼承了 ProxyFactory 的父類 ProxyCreatorSupport,具有了創(chuàng)建代理類的能力,同時實現(xiàn)了 FactoryBean 接口,當通過 getObject 方法獲得 Bean 時,將得到代理類。
2.5 Target
在之前的演示代碼中,我們直接為 ProxyFactory 指定一個對象為 Target。在 ProxyFactoryBean 中不僅能使用這種方式,還可以通過 TargetSource 的形式指定。
TargetSource 相當于為對象進行了一層封裝,ProxyFactoryBean 將通過 TargetSource 的 getTarget 方法來獲得目標對象。于是,我們可以通過 getTarget 方法來控制獲得的目標對象。TargetSource 的幾種實現(xiàn)類有:
(1)SingletonTargetSource
很簡單,內(nèi)部只持有一個目標對象,直接返回。和我們直接指定對象的效果是一樣的。
(2)PrototypeTargetSource
每次將返回一個新的目標對象實例。
(3)HotSwappableTartgetSource
運行時,根據(jù)特定條件,動態(tài)替換目標對象類的具體實現(xiàn)。例如當一個數(shù)據(jù)源掛了,可以切換至另外一個。
(4)CommonsPool2TargetSource
返回有限數(shù)目的目標對象實例,類似一個對象池。
(5)ThreadLocalTargetSource
為不同線程調(diào)用提供不同目標對象
2.6 Joinpoint
終于到了最后的 Joinpoint,我們通過下面的示例來理解 Joinpoint 的工作機制。
MockTask?task?=?new?MockTask();
ProxyFactory?weaver?=?new?ProxyFactory(task);
weaver.setInterfaces(new?Class[]{ITask.class});
PrepareResourceBeforeAdvice?beforeAdvice?=?new?PrepareResourceBeforeAdvice();
ResourceCleanAfterReturningAdvice?afterAdvice?=?new?ResourceCleanAfterReturningAdvice();
weaver.addAdvice(beforeAdvice);
weaver.addAdvice(afterAdvice);
ITask?proxyObject?=?(ITask)?weaver.getProxy();
proxyObject.execute();
/**?output
準備資源
開始執(zhí)行任務
任務完成
資源清理
**/
我們知道 getProxy 會通過動態(tài)代理生成一個 ITask 的接口類,那么 execute 方法的內(nèi)部是如何先執(zhí)行了 beforeAdvice 的 before 方法,接著執(zhí)行 task 的 execute 方法,再執(zhí)行 afterAdvice 的 after 方法呢?
答案就在生成的代理類中。在動態(tài)代理中,代理類方法調(diào)用的邏輯由 InvocationHandler 實例的 invoke 方法決定,那答案進一步鎖定在 invoke 方法。
在本示例中,ProxyFactory.getProxy ?會調(diào)用 ?JdkDynamicAopProxy.getProxy ?獲取代理類。
//?JdkDynamicAopProxy
public?Object?getProxy(@Nullable?ClassLoader?classLoader)?{
????if?(logger.isTraceEnabled())?{
????????logger.trace("Creating?JDK?dynamic?proxy:?"?+?this.advised.getTargetSource());
????}
????Class>[]?proxiedInterfaces?=?AopProxyUtils.completeProxiedInterfaces(this.advised,?true);
????findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
????return?Proxy.newProxyInstance(classLoader,?proxiedInterfaces,?this);
}
在 getProxy 中為 newProxyInstance 的 InvocationHandler 參數(shù)傳入 this,即 JdkDynamicAopProxy 就是一個 InvocationHandler 的實現(xiàn),其 invoke 方法如下:
//?JdkDynamicAopProxy
public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?throws?Throwable?{
????//?通過advised(創(chuàng)建對象時初始化)獲得指定的advice
????//?會將advice用相應的MethodInterceptor封裝下
????List首先獲得指定的 advice,這里包含 beforeAdvice 和 afterAdvice 實例,但會用 MethodInterceptor 封裝一層,為了后面的攔截鏈。
再創(chuàng)建一個 RelectiveMethodInvocation 對象,最后通過 proceed 進入攔截鏈。
RelectiveMethodInvocation 就是 Spring AOP 中 Joinpoint 的一個實現(xiàn),其類圖如下:

首先看下 RelectiveMethodInvocation 的構造函數(shù):
protected?ReflectiveMethodInvocation(
???Object?proxy,?@Nullable?Object?target,?Method?method,?@Nullable?Object[]?arguments,
???@Nullable?Class>?targetClass,?List?{
??this.proxy?=?proxy;
??this.target?=?target;
??this.targetClass?=?targetClass;
??this.method?=?BridgeMethodResolver.findBridgedMethod(method);
??this.arguments?=?AopProxyUtils.adaptArgumentsIfNecessary(method,?arguments);
??this.interceptorsAndDynamicMethodMatchers?=?interceptorsAndDynamicMethodMatchers;
?}
做了些相關屬性的賦值,然后看向 proceed 方法,如何調(diào)用目標對象和攔截器。
public?Object?proceed()?throws?Throwable?{
???//?currentInterceptorIndex從-1開始
???//?當達到已調(diào)用了所有的攔截器后,通過invokeJoinpoint調(diào)用目標對象的方法
???if?(this.currentInterceptorIndex?==?this.interceptorsAndDynamicMethodMatchers.size()?-?1)?{
??????return?invokeJoinpoint();
???}
???//?獲得攔截器,調(diào)用其invoke方法
???//?currentInterceptorIndex加1
???Object?interceptorOrInterceptionAdvice?=
?????????this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
????return?((MethodInterceptor)?interceptorOrInterceptionAdvice).invoke(this);
}
currentInterceptorIndex 從 -1 開始,interceptorsAndDynamicMethodMatchers 里有兩個攔截器,再由于減 1,所有調(diào)用目標對象方法的條件是currentInterceptorIndex 等于 1。
首先由于 -1 != 1,會獲得包含了 beforeAdvice 的 MethodBeforeAdviceInterceptor 實例, currentInterceptorIndex 加 1 變?yōu)?0。調(diào)用其 invoke 方法,由于是 Before-Advice,所以先執(zhí)行 beforeAdvice 的 before 方法,然后調(diào)用 proceed 進入攔截鏈的下一環(huán)。
//?MethodBeforeAdviceInterceptor
public?Object?invoke(MethodInvocation?mi)?throws?Throwable?{
????this.advice.before(mi.getMethod(),?mi.getArguments(),?mi.getThis());
????return?mi.proceed();
}
又回到了 proceed 方法,0 != 1,再次獲得 advice,這次獲得的是包含 afterAdvice 的 AfterReturningAdviceInterceptor實例, currentInterceptorIndex 加 1 變?yōu)?1。調(diào)用其 invoke 方法,由于是 After-Returning-Adivce,所以會先執(zhí)行 proceed 進入攔截鏈的下一環(huán)。
//?AfterReturningAdviceInterceptor
public?Object?invoke(MethodInvocation?mi)?throws?Throwable?{
????Object?retVal?=?mi.proceed();
????this.advice.afterReturning(retVal,?mi.getMethod(),?mi.getArguments(),?mi.getThis());
????return?retVal;
}
再次來到 proceed 方法,1 == 1,已調(diào)用完所有的攔截器,將執(zhí)行目標對象的方法。然后 return 返回,回到 invoke 中,調(diào)用 afterAdvice 的 afterReturning。
所以在 Joinpoint 的實現(xiàn)中,通過 MethodInterceptor 完成了 目標對象方法和 Advice 的先后執(zhí)行。
3. 小結
在了解了 Spring AOP 的實現(xiàn)后,筆者對 AOP 的概念更加清晰了。在學習過程中最令筆者感興趣的是 Joinpoint 的攔截鏈,一開始不知道是怎么實現(xiàn)的,覺得很神奇 ? 。最后學完了,總結下,好像也很簡單,通過攔截器的 invoke 方法和MethodInvocation.proceed 方法(進入下一個攔截器)的相互調(diào)用。好像就這么回事。?
