關於Spring AOP,除了動態代理、CGLIB,你還知道什麼?

Spring 作爲 Java 中最流行的框架,主要歸功於其提供的 IOC 和 AOP 功能。本文將討論 Spring AOP 的實現。第一節將介紹 AOP 的相關概念,若熟悉可跳過,第二節中結合源碼介紹 Spring 是如何實現 AOP 的各概念。

1. AOP 概念

1.1 JoinPoint

進行織入操作的程序執行點。

常見類型:

  • 方法調用(Method Call):某個方法被調用的時點。

  • 方法調用執行(Method Call Execution):某個方法內部開始執行的時點。

    方法調用是在調用對象上的執行點,方法調用執行是在被調用對象的方法開始執行點。

  • 構造方法調用(Constructor Call):對某個對象調用其構造方法的時點。

  • 構造方法執行(Constructor Call Execution):某個對象構造方法內部開始執行的時點。

  • 字段設置(Field Set):某個字段通過 setter 方法被設置或直接被設置的時點。

  • 字段獲取(Field Get):某個字段通過 getter 方法被訪問或直接被訪問的時點。

  • 異常處理執行(Exception Handler Execution):某些類型異常拋出後,異常處理邏輯執行的時點。

  • 類初始化(Class Initialization):類中某些靜態類型或靜態塊的初始化時點。

1.2 Pointcut

Jointpoint 的表述方式。

常見表述方式:

  • 直接指定 Joinpoint 所在方法名稱
  • 正則表達式
  • 特定的 Pointcut 表述語言

1.3 Advice

單一橫切關注點邏輯的載體,織入到 Joinpoint 的橫切邏輯。

具體形式:

  • Before Advice:Joinpoint 處之前執行。
  • After Advice:Joinpoint 處之後執行,細分爲三種:
    • After Returning Advice:Joinpoint 處正常完成後執行。
    • After Throwing Advice:Joinpoint 處拋出異常後執行。
    • After Finally Advice:Joinpoint 處正常完成或拋出異常後執行。
  • Around Advice:包裹 Joinpoint,在 Joinpoint 之前和之後執行,具有 Before Advice 和 After Advice 的功能。
  • Introduction:爲原有的對象添加新的屬性或行爲。

1.4 Aspect

對橫切關注點邏輯進行模塊化封裝的 AOP 概念實體,包含多個 Pointcut 和相關 Advice 的定義。

1.5 織入和織入器

織入:將 Aspect 模塊化的橫切關注點集成到 OOP 系統中。

織入器:用於完成織入操作。

1.6 Target

在織入過程中被織入橫切邏輯的對象。

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

AOP各個概念所處的場景

在瞭解 AOP 的各種概念後,下面將介紹 Spring 中 AOP 概念的具體實現。

2. Spring 中的實現

前文提到 AOP 的 Joinpoint 有多種類型,方法調用、方法執行、字段設置、字段獲取等。而在 Spring AOP 中,僅支持方法執行類型的 Joinpoint,但這樣已經能滿足 80% 的開發需要,如果有特殊需求,可求助其他 AOP 產品,如 AspectJ。由於 Joinpoint 涉及運行時的過程,相當於組裝好所有部件讓 AOP 跑起來的最後一步。所以將介紹完其他概念實現後,最後介紹 Joinpoint 的實現。

2.1 Pointcut

由於 Spring AOP 僅支持方法執行類別的 Joinpoint,因此 Pointcut 需要定義被織入的方法,又因爲 Java 中方法封裝在類中,所以 Pointcut 需要定義被織入的類和方法,下面看其實現。

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
   // 不考慮具體方法參數
   boolean matches(Method method, Class<?> targetClass);
    
   // 匹配被織入的方法,匹配成功返回true,失敗返回false
   // 考慮具體方法參數,對參數進行匹配檢查
   boolean matches(Method method, Class<?> targetClass, Object... args);
   
   // 一個標誌方法
   // false表示不考慮參數,使用第一個matches方法匹配
   // true表示考慮參數,使用第二個matches方法匹配
   boolean isRuntime();

   // TrueMethodMatcher的單例對象,默認匹配所有方法
   MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;

}

看到 matches 方法的聲明,你是否會覺得有點奇怪,在 ClassFilter 中不是已經對類進行匹配了嗎,那爲什麼在 MethodMatchermatches 方法中還有一個 Class<?> targetClass 參數。請注意,這裏的 Class<?>類型參數將不會進行匹配,而僅是爲了找到具體的方法。例如:

public boolean matches(Method method, Class<?> targetClass) {
    Method targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
    ...
}

MethodMatcher相比ClassFilter特殊在有兩個 matches 方法。將根據 isRuntime()的返回結果決定調用哪個。而MethodMatcherisRuntime()分爲兩個抽象類 StaticMethodMatcher(返回false,不考慮參數)和 DynamicMethodMatcher(返回true,考慮參數)。

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

Pointcut相關類圖

DynamicMethodMatcherPointcut 本文將不介紹,主要介紹下類圖中列出的三個實現類。

(1)NameMatchMethodPointcut

通過指定方法名稱,然後與方法的名稱直接進行匹配,還支持 “*” 通配符。

public class NameMatchMethodPointcut extends StaticMethodMatcherPointcut implements Serializable {
    
    // 方法名稱
    private List<String> 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) {
            // 根據方法名匹配,isMatch提供“*”通配符支持
            if (mappedName.equals(method.getName()) || isMatch(method.getName(), mappedName)) {
                return true;
            }
        }
        return false;
    }
    
    // ...
}

(2)JdkRegexpMethodPointcut

內部有一個 Pattern 數組,通過指定正則表達式,然後和方法名稱進行匹配。

(3)AnnotationMatchingPointcut

根據目標對象是否存在指定類型的註解進行匹配。

2.2 Advice

Advice 爲橫切邏輯的載體,Spring AOP 中關於 Advice 的接口類圖如下所示:

Advice相關類圖

(1)MethodBeforeAdvice

橫切邏輯將在 Joinpoint 方法之前執行。可用於進行資源初始化或準備性工作。

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

下面來實現一個 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 的實現類 MockTask

public class MockTask implements ITask {
    
   @Override
   public void execute() {
      System.out.println("開始執行任務");
      System.out.println("任務完成");
   }
    
}

Main 方法如下,ProxyFactoryAdvisor 在後續會進行介紹,先簡單瞭解下,通過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});
      // 內含一個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:
準備資源
開始執行任務
任務完成
**/

可以看出在執行代理對象 proxyObjectexecute 方法時,先執行了 PrepareResourceBeforeAdvice 中的 before 方法。

(2)ThrowsAdvice

橫切邏輯將在 Joinpoint 方法拋出異常時執行。可用於進行異常監控工作。

ThrowsAdvice 接口未定義任何方法,但約定在實現該接口時,定義的方法需符合如下規則

void afterThrowing([Method, args, target], ThrowableSubclass)

前三個參數爲 Joinpoint 的相關信息,可省略。ThrowableSubclass 指定需要攔截的異常類型。

例如可定義多個 afterThrowing 方法捕獲異常:

public class ExceptionMonitorThrowsAdvice implements ThrowsAdvice {
    
    public void afterThrowing(Throwable t) {
        System.out.println("發生【普通異常】");
    }
    
    public void afterThrowing(RuntimeException e) {
        System.out.println("發生【運行時異常】");
    }
    
    public void afterThrowing(Method m, Object[] args, Object target, ApplicationException e) {
        System.out.println(target.getClass() + m.getName() + "發生【應用異常】");
    }
    
}

修改下 MockTask 的內容:

public class MockTask implements ITask {
    
    @Override
    public void execute() {
        System.out.println("開始執行任務");
        // 拋出一個自定義的應用異常
        throw new ApplicationException();
        // System.out.println("任務完成");
    }
    
}

修改下 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});
        NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor();
        advisor.setMappedName("execute");
        // 指定異常監控Advice
        advisor.setAdvice(new ExceptionMonitorThrowsAdvice());
        weaver.addAdvisor(advisor);
        ITask proxyObject = (ITask) weaver.getProxy();
        proxyObject.execute();
    }
    
}

/** output:
開始執行任務
class com.chaycao.spring.aop.MockTaskexecute發生【應用異常】
**/

當拋出 ApplicationException 時,被相應的 afterThrowing 方法捕獲到。

(3)AfterReturningAdvice

橫切邏輯將在 Joinpoint 方法正常返回時執行。可用於處理資源清理工作。

public interface AfterReturningAdvice extends AfterAdvice {
    
   void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable;
    
}

實現一個資源清理的 Advice :

public class ResourceCleanAfterReturningAdvice implements AfterReturningAdvice {
    
   @Override
   public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
      System.out.println("資源清理");
   }
    
}

修改 MockTask 爲正常執行成功, 修改 Main 方法爲指定 ResourceCLeanAfterReturningAdvice,效果如下:

/** output:
開始執行任務
任務完成
資源清理
**/

(4)MethodInterceptor

相當於 Around Advice,功能十分強大,可在 Joinpoint 方法前後執行,甚至修改返回值。其定義如下:

public interface MethodInterceptor extends Interceptor {
    
    Object invoke(MethodInvocation invocation) throws Throwable;
    
}

MethodInvocation 是對 Method 的封裝,通過 proceed() 對方法進行調用。下面舉個例子:

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("監控異常");
         return null;
      } finally {
         System.out.println("資源清理");
      }
   }
   
}

上面實現的 invoke 方法,一下子把前面說的三種功能都實現了。

以上 4 種 Advice 會在目標對象類的所有實例上生效,被稱爲 per-class 類型的 Advice。還有一種 per-instance 類型的 Advice,可爲實例添加新的屬性或行爲,也就是第一節提到的 Introduction。

(5)Introduction

Spring 爲目標對象添加新的屬性或行爲,需要聲明接口和其實現類,然後通過攔截器將接口的定義和實現類的實現織入到目標對象中。我們認識下 DelegatingIntroductionInterceptor,其作爲攔截器,當調用新行爲時,會委派(delegate)給實現類來完成。

例如,想在原 MockTask 上進行加強,但不修改類的聲明,可聲明一個新的接口 IReinfore

public interface IReinforce {
   String name = "增強器";
   void fun();
}

再聲明一個接口的實現類:

public class ReinforeImpl implements IReinforce {

    @Override
    public void fun() {
        System.out.println("我變強了,能執行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});
      // 爲攔截器指定需要委託的實現類的實例
      DelegatingIntroductionInterceptor delegatingIntroductionInterceptor =
            new DelegatingIntroductionInterceptor(new ReinforeImpl());
      weaver.addAdvice(delegatingIntroductionInterceptor);
      ITask proxyObject = (ITask) weaver.getProxy();
      proxyObject.execute();
      // 使用IReinfore接口調用新的屬性和行爲
      IReinforce reinforeProxyObject = (IReinforce) weaver.getProxy();
      System.out.println("通過使用" + reinforeProxyObject.name);
      reinforeProxyObject.fun();
   }
   
}

/** output:
開始執行任務
任務完成
通過使用增強器
我變強了,能執行fun方法了
**/

代理對象 proxyObject 便通過攔截器,可以使用 ReinforeImpl 實現類的方法。

2.3 Aspect

Spring 中用 Advisor 表示 Aspect,不同之處在於 Advisor 通常只持有一個 Pointcut一個 AdviceAdvisor 根據 Advice 分爲 PointcutAdvisorIntroductionAdvisor

2.3.1 PointcutAdvisor

常用的 PointcutAdvisor 實現類有:

(1) DefaultPointcutAdvisor

最通用的實現類,可以指定任意類型的 Pointcut除了 Introduction 外的任意類型 Advice

Pointcut pointcut = ...; // 任意類型的Pointcut
Advice advice = ...; // 除了Introduction外的任意類型Advice
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
advisor.setPointcut(pointcut);
advisor.setAdvice(advice);

(2)NameMatchMethodPointcutAdvisor

在演示 Advice 的代碼中,已經有簡單介紹過,內部有一個 NameMatchMethodPointcut 的實例,可持有除 Introduction 外的任意類型 Advice

Advice advice = ...; // 除了Introduction外的任意類型Advice
NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor();
advisor.setMappedName("execute");
advisor.setAdvice(advice);

(3)RegexpMethodPointcutAdvisor

內部有一個 RegexpMethodPointcut 的實例。

2.3.2 IntroductionAdvisor

只能支持類級別的攔截,和 Introduction 類型的 Advice。實現類有 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 生成代理對象方式有:

  • 如果目標類實現了某些接口,默認通過動態代理生成。
  • 如果目標類沒有實現接口,默認通過CGLIB生成。
  • 也可以直接設置ProxyFactory生成的方式,即使實現了接口,也能使用CGLIB。

在之前的演示代碼中,我們沒有啓動 Spring 容器,也就是沒有使用 Spring IOC 功能,而是獨立使用了 Spring AOP。那麼 Spring AOP 是如何與 Spring IOC 進行整合的?是採用了 Spring 整合最常用的方法 —— FactoryBean

ProxyFactoryBean 繼承了 ProxyFactory 的父類 ProxyCreatorSupport,具有了創建代理類的能力,同時實現了 FactoryBean 接口,當通過 getObject 方法獲得 Bean 時,將得到代理類。

2.5 Target

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

TargetSource 相當於爲對象進行了一層封裝,ProxyFactoryBean 將通過 TargetSourcegetTarget 方法來獲得目標對象。於是,我們可以通過 getTarget 方法來控制獲得的目標對象TargetSource 的幾種實現類有:

(1)SingletonTargetSource

很簡單,內部只持有一個目標對象,直接返回。和我們直接指定對象的效果是一樣的。

(2)PrototypeTargetSource

每次將返回一個新的目標對象實例。

(3)HotSwappableTartgetSource

運行時,根據特定條件,動態替換目標對象類的具體實現。例如當一個數據源掛了,可以切換至另外一個。

(4)CommonsPool2TargetSource

返回有限數目的目標對象實例,類似一個對象池。

(5)ThreadLocalTargetSource

爲不同線程調用提供不同目標對象

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   
準備資源    
開始執行任務     
任務完成    
資源清理   
**/

我們知道 getProxy 會通過動態代理生成一個 ITask 的接口類,那麼 execute 方法的內部是如何先執行了 beforeAdvicebefore 方法,接着執行 taskexecute 方法,再執行 afterAdviceafter 方法呢?

答案就在生成的代理類中。在動態代理中,代理類方法調用的邏輯由 InvocationHandler 實例的 invoke 方法決定,那答案進一步鎖定在 invoke 方法

在本示例中,ProxyFactory.getProxy 會調用 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 參數傳入 this,即 JdkDynamicAopProxy 就是一個 InvocationHandler 的實現,其 invoke 方法如下:

// JdkDynamicAopProxy
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // 通過advised(創建對象時初始化)獲得指定的advice
    // 會將advice用相應的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 {
        // 創建一個MethodInvocation
        MethodInvocation invocation =
            new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
        // 調用procced,開始進入攔截鏈(執行目標對象方法和MethodInterceptor的advice)
        retVal = invocation.proceed();
    }
    return retVal;
}

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

再創建一個 RelectiveMethodInvocation 對象,最後通過 proceed 進入攔截鏈。

RelectiveMethodInvocation 就是 Spring AOP 中 Joinpoint 的一個實現,其類圖如下:

Joinpoint類圖

首先看下 RelectiveMethodInvocation 的構造函數:

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

做了些相關屬性的賦值,然後看向 proceed 方法,如何調用目標對象和攔截器。

public Object proceed() throws Throwable {
    // currentInterceptorIndex從-1開始
    // 當達到已調用了所有的攔截器後,通過invokeJoinpoint調用目標對象的方法
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return invokeJoinpoint();
    }
    // 獲得攔截器,調用其invoke方法
    // currentInterceptorIndex加1
    Object interceptorOrInterceptionAdvice =
         this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}

currentInterceptorIndex 從 -1 開始,interceptorsAndDynamicMethodMatchers 裏有兩個攔截器,再由於減 1,所有調用目標對象方法的條件是currentInterceptorIndex 等於 1。

首先由於 -1 != 1,會獲得包含了 beforeAdviceMethodBeforeAdviceInterceptor 實例, currentInterceptorIndex 加 1 變爲 0。調用其 invoke 方法,由於是 Before-Advice,所以先執行 beforeAdvicebefore 方法,然後調用 proceed 進入攔截鏈的下一環。

// 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 變爲 1。調用其 invoke 方法,由於是 After-Returning-Adivce,所以會先執行 proceed 進入攔截鏈的下一環。

// 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,已調用完所有的攔截器,將執行目標對象的方法。 然後 return 返回,回到 invoke 中,調用 afterAdviceafterReturning

所以在 Joinpoint 的實現中,通過 MethodInterceptor 完成了 目標對象方法和 Advice 的先後執行。

3. 小結

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章