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

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

DynamicMethodMatcherPointcut 本文將不介紹,主要介紹下類圖中列出的三個(gè)實(shí)現(xiàn)類。
(1)NameMatchMethodPointcut
通過(guò)指定方法名稱,然后與方法的名稱直接進(jìn)行匹配,還支持 “*” 通配符。
public?class?NameMatchMethodPointcut?extends?StaticMethodMatcherPointcut?implements?Serializable?{
????
?//?方法名稱
?private?List?mappedNames?=?new?ArrayList<>();
?//?設(shè)置方法名稱
?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)部有一個(gè) Pattern 數(shù)組,通過(guò)指定正則表達(dá)式,然后和方法名稱進(jìn)行匹配。
(3)AnnotationMatchingPointcut
根據(jù)目標(biāo)對(duì)象是否存在指定類型的注解進(jìn)行匹配。
2.2 Advice
Advice 為橫切邏輯的載體,Spring AOP 中關(guān)于 Advice 的接口類圖如下所示:

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

首先看下 RelectiveMethodInvocation 的構(gòu)造函數(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;
?}
做了些相關(guān)屬性的賦值,然后看向 proceed 方法,如何調(diào)用目標(biāo)對(duì)象和攔截器。
public?Object?proceed()?throws?Throwable?{
???//?currentInterceptorIndex從-1開(kāi)始
???//?當(dāng)達(dá)到已調(diào)用了所有的攔截器后,通過(guò)invokeJoinpoint調(diào)用目標(biāo)對(duì)象的方法
???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 開(kāi)始,interceptorsAndDynamicMethodMatchers 里有兩個(gè)攔截器,再由于減 1,所有調(diào)用目標(biāo)對(duì)象方法的條件是currentInterceptorIndex 等于 1。
首先由于 -1 != 1,會(huì)獲得包含了 beforeAdvice 的 MethodBeforeAdviceInterceptor 實(shí)例, currentInterceptorIndex 加 1 變?yōu)?0。調(diào)用其 invoke 方法,由于是 Before-Advice,所以先執(zhí)行 beforeAdvice 的 before 方法,然后調(diào)用 proceed 進(jìn)入攔截鏈的下一環(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實(shí)例, currentInterceptorIndex 加 1 變?yōu)?1。調(diào)用其 invoke 方法,由于是 After-Returning-Adivce,所以會(huì)先執(zhí)行 proceed 進(jìn)入攔截鏈的下一環(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;
}
再次來(lái)到 proceed 方法,1 == 1,已調(diào)用完所有的攔截器,將執(zhí)行目標(biāo)對(duì)象的方法。然后 return 返回,回到 invoke 中,調(diào)用 afterAdvice 的 afterReturning。
所以在 Joinpoint 的實(shí)現(xiàn)中,通過(guò) MethodInterceptor 完成了 目標(biāo)對(duì)象方法和 Advice 的先后執(zhí)行。
3. 小結(jié)
在了解了 Spring AOP 的實(shí)現(xiàn)后,筆者對(duì) AOP 的概念更加清晰了。在學(xué)習(xí)過(guò)程中最令筆者感興趣的是 Joinpoint 的攔截鏈,一開(kāi)始不知道是怎么實(shí)現(xiàn)的,覺(jué)得很神奇 ? 。最后學(xué)完了,總結(jié)下,好像也很簡(jiǎn)單,通過(guò)攔截器的 invoke 方法和MethodInvocation.proceed 方法(進(jìn)入下一個(gè)攔截器)的相互調(diào)用。好像就這么回事。?
完
? ? ? ?
???覺(jué)得不錯(cuò),點(diǎn)個(gè)在看~

