日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

关于Spring AOP,除了动态代理、CGLIB,你还知道什么?

發(fā)布時(shí)間:2024/8/23 javascript 50 豆豆
生活随笔 收集整理的這篇文章主要介紹了 关于Spring AOP,除了动态代理、CGLIB,你还知道什么? 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

來源 |?草捏子

責(zé)編 | Carol

封圖?| CSDN 付費(fèi)下載于視覺中國

Spring 作為 Java 中最流行的框架,主要?dú)w功于其提供的 IOC 和 AOP 功能。本文將討論 Spring AOP 的實(shí)現(xiàn)。第一節(jié)將介紹 AOP 的相關(guān)概念,若熟悉可跳過,第二節(jié)中結(jié)合源碼介紹 Spring 是如何實(shí)現(xiàn) AOP 的各概念。

AOP概念

1.1 JoinPoint

進(jìn)行織入操作的程序執(zhí)行點(diǎn)。

常見類型:

  • 方法調(diào)用(Method Call):某個(gè)方法被調(diào)用的時(shí)點(diǎn)。

  • 方法調(diào)用執(zhí)行(Method Call Execution):某個(gè)方法內(nèi)部開始執(zhí)行的時(shí)點(diǎn)。

    方法調(diào)用是在調(diào)用對(duì)象上的執(zhí)行點(diǎn),方法調(diào)用執(zhí)行是在被調(diào)用對(duì)象的方法開始執(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)部開始執(zhí)行的時(shí)點(diǎn)。

  • 字段設(shè)置(Field Set):某個(gè)字段通過 setter 方法被設(shè)置或直接被設(shè)置的時(shí)點(diǎn)。

  • 字段獲取(Field Get):某個(gè)字段通過 getter 方法被訪問或直接被訪問的時(shí)點(diǎn)。

  • 異常處理執(zhí)行(Exception Handler Execution):某些類型異常拋出后,異常處理邏輯執(zhí)行的時(shí)點(diǎn)。

  • 類初始化(Class Initialization):類中某些靜態(tài)類型或靜態(tài)塊的初始化時(shí)點(diǎn)。

1.2 Pointcut

Jointpoint 的表述方式。

常見表述方式:

  • 直接指定 Joinpoint 所在方法名稱

  • 正則表達(dá)式

  • 特定的 Pointcut 表述語言

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

在織入過程中被織入橫切邏輯的對(duì)象。

將上述 6 個(gè)概念放在一塊,如下圖所示:

AOP各個(gè)概念所處的場(chǎng)景

在了解 AOP 的各種概念后,下面將介紹 Spring 中 AOP 概念的具體實(shí)現(xiàn)。

Spring 中的實(shí)現(xiàn)

前文提到 AOP 的 Joinpoint 有多種類型,方法調(diào)用、方法執(zhí)行、字段設(shè)置、字段獲取等。而在 Spring AOP 中,僅支持方法執(zhí)行類型的 Joinpoint,但這樣已經(jīng)能滿足 80% 的開發(fā)需要,如果有特殊需求,可求助其他 AOP 產(chǎn)品,如 AspectJ。由于 Joinpoint 涉及運(yùn)行時(shí)的過程,相當(dāng)于組裝好所有部件讓 AOP 跑起來的最后一步。所以將介紹完其他概念實(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?通過?ClassFilter?和?MethodMatcher?的組合來定義相應(yīng)的 Joinpoint。Pointcut?將類和方法拆開來定義,是為了能夠重用。例如有兩個(gè) Joinpoint,分別是 A 類的?fun()?方法和 B 類的?fun()?方法,兩個(gè)方法簽名相同,則只需一個(gè)?fun()?方法的?MethodMatcher?對(duì)象,達(dá)到了重用的目的,ClassFilter?同理。

下面了解下?ClassFilter?和?MethodMatcher如何進(jìn)行匹配

ClassFilter?使用matches方法匹配被織入的類,定義如下:

public?interface?ClassFilter?{//?匹配被織入的類,匹配成功返回true,失敗返回falseboolean?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ì)覺得有點(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)類圖如下所示:

Pointcut相關(guān)類圖

DynamicMethodMatcherPointcut?本文將不介紹,主要介紹下類圖中列出的三個(gè)實(shí)現(xiàn)類。

(1)NameMatchMethodPointcut

通過指定方法名稱,然后與方法的名稱直接進(jìn)行匹配,還支持 “*” 通配符。

public?class?NameMatchMethodPointcut?extends?StaticMethodMatcherPointcut?implements?Serializable?{//?方法名稱private?List<String>?mappedNames?=?new?ArrayList<>();//?設(shè)置方法名稱public?void?setMappedNames(String...?mappedNames)?{this.mappedNames?=?new?ArrayList<>(Arrays.asList(mappedNames));}@Overridepublic?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ù)組,通過指定正則表達(dá)式,然后和方法名稱進(jìn)行匹配。

(3)AnnotationMatchingPointcut

根據(jù)目標(biāo)對(duì)象是否存在指定類型的注解進(jìn)行匹配。

2.2 Advice

Advice 為橫切邏輯的載體,Spring AOP 中關(guān)于 Advice 的接口類圖如下所示:

Advice相關(guān)類圖

(1)MethodBeforeAdvice

橫切邏輯將在?Joinpoint 方法之前執(zhí)行。可用于進(jìn)行資源初始化或準(zhǔn)備性工作。

public?interface?MethodBeforeAdvice?extends?BeforeAdvice?{void?before(Method?method,?Object[]?args,?@Nullable?Object?target)?throws?Throwable;}

下面來實(shí)現(xiàn)一個(gè)?MethodBeforeAdvice,看下其效果。

public?class?PrepareResourceBeforeAdvice?implements?MethodBeforeAdvice?{@Overridepublic?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?{@Overridepublic?void?execute()?{System.out.println("開始執(zhí)行任務(wù)");System.out.println("任務(wù)完成");}}

Main 方法如下,ProxyFactory、Advisor?在后續(xù)會(huì)進(jìn)行介紹,先簡(jiǎn)單了解下,通過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è)NameMatchMethodPointcutNameMatchMethodPointcutAdvisor?advisor?=?new?NameMatchMethodPointcutAdvisor();//?指定NameMatchMethodPointcut的方法名advisor.setMappedName("execute");//?指定Adviceadvisor.setAdvice(new?PrepareResourceBeforeAdvice());weaver.addAdvisor(advisor);ITask?proxyObject?=?(ITask)?weaver.getProxy();proxyObject.execute();}}/**?output: 準(zhǔn)備資源 開始執(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?{@Overridepublic?void?execute()?{System.out.println("開始執(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)控Adviceadvisor.setAdvice(new?ExceptionMonitorThrowsAdvice());weaver.addAdvisor(advisor);ITask?proxyObject?=?(ITask)?weaver.getProxy();proxyObject.execute();}}/**?output: 開始執(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?{@Overridepublic?void?afterReturning(Object?returnValue,?Method?method,?Object[]?args,?Object?target)?throws?Throwable?{System.out.println("資源清理");}}

修改?MockTask?為正常執(zhí)行成功, 修改?Main?方法為指定?ResourceCLeanAfterReturningAdvice,效果如下:

/**?output: 開始執(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?的封裝,通過?proceed()?對(duì)方法進(jìn)行調(diào)用。下面舉個(gè)例子:

public?class?AroundMethodInterceptor?implements?MethodInterceptor?{@Overridepublic?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 方法,一下子把前面說的三種功能都實(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)類,然后通過攔截器將接口的定義和實(shí)現(xiàn)類的實(shí)現(xiàn)織入到目標(biāo)對(duì)象中。我們認(rèn)識(shí)下?DelegatingIntroductionInterceptor,其作為攔截器,當(dāng)調(diào)用新行為時(shí),會(huì)委派(delegate)給實(shí)現(xiàn)類來完成。

例如,想在原?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?{@Overridepublic?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("通過使用"?+?reinforeProxyObject.name);reinforeProxyObject.fun();}}/**?output: 開始執(zhí)行任務(wù) 任務(wù)完成 通過使用增強(qiáng)器 我變強(qiáng)了,能執(zhí)行fun方法了 **/

代理對(duì)象?proxyObject?便通過攔截器,可以使用?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)單介紹過,內(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)通過動(dòng)態(tài)代理生成。

  • 如果目標(biāo)類沒有實(shí)現(xiàn)接口,默認(rèn)通過CGLIB生成。

  • 也可以直接設(shè)置ProxyFactory生成的方式,即使實(shí)現(xiàn)了接口,也能使用CGLIB。

在之前的演示代碼中,我們沒有啟動(dòng) Spring 容器,也就是沒有使用 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)通過?getObject?方法獲得 Bean 時(shí),將得到代理類。

2.5 Target

在之前的演示代碼中,我們直接為?ProxyFactory?指定一個(gè)對(duì)象為 Target。在?ProxyFactoryBean?中不僅能使用這種方式,還可以通過?TargetSource?的形式指定。

TargetSource?相當(dāng)于為對(duì)象進(jìn)行了一層封裝,ProxyFactoryBean?將通過?TargetSource?的?getTarget?方法來獲得目標(biāo)對(duì)象。于是,我們可以通過?getTarget?方法來控制獲得的目標(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,我們通過下面的示例來理解 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)備資源 開始執(zhí)行任務(wù) 任務(wù)完成 資源清理 **/

我們知道?getProxy?會(huì)通過動(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?{//?通過advised(創(chuàng)建對(duì)象時(shí)初始化)獲得指定的advice//?會(huì)將advice用相應(yīng)的MethodInterceptor封裝下List<Object>?chain?=?this.advised.getInterceptorsAndDynamicInterceptionAdvice(method,?targetClass);if?(chain.isEmpty())?{Object[]?argsToUse?=?AopProxyUtils.adaptArgumentsIfNecessary(method,?args);retVal?=?AopUtils.invokeJoinpointUsingReflection(target,?method,?argsToUse);}else?{//?創(chuàng)建一個(gè)MethodInvocationMethodInvocation?invocation?=new?ReflectiveMethodInvocation(proxy,?target,?method,?args,?targetClass,?chain);//?調(diào)用procced,開始進(jìn)入攔截鏈(執(zhí)行目標(biāo)對(duì)象方法和MethodInterceptor的advice)retVal?=?invocation.proceed();}return?retVal; }

首先獲得指定的 advice,這里包含?beforeAdvice?和?afterAdvice?實(shí)例,但會(huì)用?MethodInterceptor?封裝一層,為了后面的攔截鏈。

再創(chuàng)建一個(gè)?RelectiveMethodInvocation?對(duì)象,最后通過?proceed?進(jìn)入攔截鏈。

RelectiveMethodInvocation?就是 Spring AOP 中 Joinpoint 的一個(gè)實(shí)現(xiàn),其類圖如下:

Joinpoint類圖

首先看下?RelectiveMethodInvocation?的構(gòu)造函數(shù):

protected?ReflectiveMethodInvocation(Object?proxy,?@Nullable?Object?target,?Method?method,?@Nullable?Object[]?arguments,@Nullable?Class<?>?targetClass,?List<Object>?interceptorsAndDynamicMethodMatchers)?{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開始//?當(dāng)達(dá)到已調(diào)用了所有的攔截器后,通過invokeJoinpoint調(diào)用目標(biāo)對(duì)象的方法if?(this.currentInterceptorIndex?==?this.interceptorsAndDynamicMethodMatchers.size()?-?1)?{return?invokeJoinpoint();}//?獲得攔截器,調(diào)用其invoke方法//?currentInterceptorIndex加1Object?interceptorOrInterceptionAdvice?=this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);return?((MethodInterceptor)?interceptorOrInterceptionAdvice).invoke(this); }

currentInterceptorIndex?從 -1 開始,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; }

再次來到?proceed?方法,1 == 1,已調(diào)用完所有的攔截器,將執(zhí)行目標(biāo)對(duì)象的方法。然后 return 返回,回到?invoke?中,調(diào)用?afterAdvice?的?afterReturning。

所以在 Joinpoint 的實(shí)現(xiàn)中,通過?MethodInterceptor?完成了 目標(biāo)對(duì)象方法和 Advice 的先后執(zhí)行。

小結(jié)

在了解了 Spring AOP 的實(shí)現(xiàn)后,筆者對(duì) AOP 的概念更加清晰了。在學(xué)習(xí)過程中最令筆者感興趣的是 Joinpoint 的攔截鏈,一開始不知道是怎么實(shí)現(xiàn)的,覺得很神奇 ???? 。最后學(xué)完了,總結(jié)下,好像也很簡(jiǎn)單,通過攔截器的?invoke?方法和MethodInvocation.proceed?方法(進(jìn)入下一個(gè)攔截器)的相互調(diào)用。

?

推薦閱讀

  • 后端程序員必備:書寫高質(zhì)量SQL的30條建議

  • 螞蟻金服高要求的領(lǐng)域建模能力,對(duì)研發(fā)來說到底指什么?

  • Redis 6.0 新特性:多線程連環(huán) 13 問!

  • AI 修復(fù) 100 年前晚清影像喜提熱搜,有穿越內(nèi)味兒了!

  • 你現(xiàn)在從事的程序員還有多久會(huì)消失?牛津大學(xué)研究員幫你算了算

  • 一次對(duì)語音技術(shù)的徹底批判

  • 到底是哪些人在玩鏈游?| 《區(qū)塊鏈游戲玩家研究報(bào)告》

真香,朕在看了!

總結(jié)

以上是生活随笔為你收集整理的关于Spring AOP,除了动态代理、CGLIB,你还知道什么?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。