<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>

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

          共 17287字,需瀏覽 35分鐘

           ·

          2020-12-06 02:10

          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各個概念所處的場景

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

          下面了解下 ClassFilterMethodMatcher如何進行匹配

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

          Pointcut 也因 MethodMathcer 可分為 StaticMethodMatcherPointcutDynamicMethodMatcherPointcut,相關類圖如下所示:

          Pointcut相關類圖

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

          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 方法如下,ProxyFactoryAdvisor 在后續(xù)會進行介紹,先簡單了解下,通過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)含一個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í)行代理對象 proxyObjectexecute 方法時,先執(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一個 AdviceAdvisor 根據(jù) Advice 分為 PointcutAdvisorIntroductionAdvisor

          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 將通過 TargetSourcegetTarget 方法來獲得目標對象。于是,我們可以通過 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í)行了 beforeAdvicebefore 方法,接著執(zhí)行 taskexecute 方法,再執(zhí)行 afterAdviceafter 方法呢?

          答案就在生成的代理類中。在動態(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 中為 newProxyInstanceInvocationHandler 參數(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?chain?=?this.advised.getInterceptorsAndDynamicInterceptionAdvice(method,?targetClass);

          ????if?(chain.isEmpty())?{
          ????????Object[]?argsToUse?=?AopProxyUtils.adaptArgumentsIfNecessary(method,?args);
          ????????retVal?=?AopUtils.invokeJoinpointUsingReflection(target,?method,?argsToUse);
          ????}
          ????else?{
          ????????//?創(chuàng)建一個MethodInvocation
          ????????MethodInvocation?invocation?=
          ????????????new?ReflectiveMethodInvocation(proxy,?target,?method,?args,?targetClass,?chain);
          ????????//?調(diào)用procced,開始進入攔截鏈(執(zhí)行目標對象方法和MethodInterceptor的advice)
          ????????retVal?=?invocation.proceed();
          ????}
          ????return?retVal;
          }

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

          再創(chuàng)建一個 RelectiveMethodInvocation 對象,最后通過 proceed 進入攔截鏈。

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

          Joinpoint類圖

          首先看下 RelectiveMethodInvocation 的構造函數(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;
          ?}

          做了些相關屬性的賦值,然后看向 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,會獲得包含了 beforeAdviceMethodBeforeAdviceInterceptor 實例, currentInterceptorIndex 加 1 變?yōu)?0。調(diào)用其 invoke 方法,由于是 Before-Advice,所以先執(zhí)行 beforeAdvicebefore 方法,然后調(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,這次獲得的是包含 afterAdviceAfterReturningAdviceInterceptor實例, 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)用 afterAdviceafterReturning

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

          3. 小結

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


          瀏覽 51
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                    欧美色综合一区二区三区 | 色婷婷91视频 | 欧美www网站 | 9·1成长视频蘑菇视频大全 | 三级视频国产 |