<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          關(guān)于Spring AOP,除了動(dòng)態(tài)代理、CGLIB,你還知道什么?

          共 17406字,需瀏覽 35分鐘

           ·

          2020-09-23 09:12




          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各個(gè)概念所處的場(chǎng)景

          在了解 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ò) ClassFilterMethodMatcher 的組合來(lái)定義相應(yīng)的 Joinpoint。Pointcut 將類和方法拆開(kāi)來(lái)定義,是為了能夠重用。例如有兩個(gè) Joinpoint,分別是 A 類的 fun() 方法和 B 類的 fun() 方法,兩個(gè)方法簽名相同,則只需一個(gè) fun() 方法的 MethodMatcher 對(duì)象,達(dá)到了重用的目的,ClassFilter 同理。

          下面了解下 ClassFilterMethodMatcher如何進(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)行匹配了嗎,那為什么在 MethodMatchermatches 方法中還有一個(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è)。而MethodMatcherisRuntime()分為兩個(gè)抽象類 StaticMethodMatcher(返回false,不考慮參數(shù))和 DynamicMethodMatcher(返回true,考慮參數(shù))。

          Pointcut 也因 MethodMathcer 可分為 StaticMethodMatcherPointcutDynamicMethodMatcherPointcut,相關(guān)類圖如下所示:

          Pointcut相關(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 的接口類圖如下所示:

          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;
          ????
          }

          下面來(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 方法如下,ProxyFactoryAdvisor 在后續(xù)會(huì)進(jìn)行介紹,先簡(jiǎn)單了解下,通過(guò)ProxyFactory拿到代理類,Advisor用于封裝 PointcutAdvice

          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ì)象 proxyObjectexecute 方法時(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è) AdviceAdvisor 根據(jù) Advice 分為 PointcutAdvisorIntroductionAdvisor

          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ò) TargetSourcegetTarget 方法來(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í)行了 beforeAdvicebefore 方法,接著執(zhí)行 taskexecute 方法,再執(zhí)行 afterAdviceafter 方法呢?

          答案就在生成的代理類中。在動(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 中為 newProxyInstanceInvocationHandler 參數(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?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è)MethodInvocation
          ????????MethodInvocation?invocation?=
          ????????????new?ReflectiveMethodInvocation(proxy,?target,?method,?args,?targetClass,?chain);
          ????????//?調(diào)用procced,開(kāi)始進(jìn)入攔截鏈(執(zhí)行目標(biāo)對(duì)象方法和MethodInterceptor的advice)
          ????????retVal?=?invocation.proceed();
          ????}
          ????return?retVal;
          }

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

          再創(chuàng)建一個(gè) RelectiveMethodInvocation 對(duì)象,最后通過(guò) 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?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開(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ì)獲得包含了 beforeAdviceMethodBeforeAdviceInterceptor 實(shí)例, currentInterceptorIndex 加 1 變?yōu)?0。調(diào)用其 invoke 方法,由于是 Before-Advice,所以先執(zhí)行 beforeAdvicebefore 方法,然后調(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,這次獲得的是包含 afterAdviceAfterReturningAdviceInterceptor實(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)用 afterAdviceafterReturning

          所以在 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)用。好像就這么回事。?


          ? ? ? ?
          ???
          Spring Data JPA 如何讓后端開(kāi)發(fā)提高效率(小技巧)
          Spring Boot 集成 XXL-JOB 任務(wù)調(diào)度平臺(tái)
          模板方法模式——看看 JDK 和 Spring 是如何優(yōu)雅復(fù)用代碼的

          覺(jué)得不錯(cuò),點(diǎn)個(gè)在看~

          瀏覽 64
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                    <th id="afajh"><progress id="afajh"></progress></th>
                    色姑娘超碰狠狠操五月 | 国产激情精品视频 | 国产美女做爱A片是免费 | 国产 最新 视频 | 亚洲精品视频在线播放 |