主頁 > 後端開發 > Spring系列26:Spring AOP 通知與順序詳解

Spring系列26:Spring AOP 通知與順序詳解

2022-03-22 06:18:02 後端開發

本文內容

  1. 如何宣告通知
  2. 如何傳遞引數到通知方法中
  3. 多種通知多個切面的通知順序
  4. 多個切面通知的順序原始碼分析與圖解

宣告通知

Spring中有5種通知,通過對應的注解來宣告:

  • @BeforeBefore 通知,用于方法執行前增強
  • @AfterReturning :After Returning 通知,方法正常執行回傳后增強
  • @AfterThrowing:After Throwing 通知,方法執行通過拋出例外退出時
  • @After:After (Finally) 通知,方法執行退出時執行增強,不管是正常回傳,還是拋出例外退出,相當于try{}catch{}finally{}中的finally的陳述句,
  • @Around:Around 通知,最強大的通知,環繞在目標方法前后執行,它有機會在方法運行之前和之后進行作業,并確定該方法何時、如何以及是否真正開始運行

始終使用滿足您要求的最不強大的通知形式(也就是說,如果前置通知可以使用,請不要使用環繞通知),

簡單的使用通過一個綜合實體來說明,具體的通知再詳細說明,

綜合案例

目標類,2個方法,一個正常執行,一個拋出例外,

package com.crab.spring.aop.demo03.advice;

/**
 * @author zfd
 * @version v1.0
 * @date 2022/2/7 11:31
 * @關于我 請關注公眾號 螃蟹的Java筆記 獲取更多技術系列
 */
public class Service1 {
    /**
     * 正常執行的方法
     */
    public String hello(String name) {
        System.out.println("hello " + name);
        return "hello " + name + "!";
    }

    /**
     * 執行拋出例外的方法
     */
    public void throwException() {
        System.out.println("throws a runtime exception");
        throw new RuntimeException("方法執行例外了");
    }
}

切面中的通知

@Aspect // 切面
public class CommonCaseAspect {

    /**
     * 公共切點
     *  匹配Service1的所有方法
     */
    @Pointcut("execution(* com.crab.spring.aop.demo03.advice.Service1.*(..))")
    public void pc(){

    }

    /**
     * 前置通知
     */
    @Before("pc()")
    public void before(JoinPoint joinpoint){
        System.out.println("Before: " +  joinpoint);
    }

    /**
     * 回傳通知
     */
    @AfterReturning("pc()")
    public void afterReturning(JoinPoint joinpoint){
        System.out.println("AfterReturning: " +  joinpoint);
    }

    /**
     * 拋出例外通知
     */
    @AfterThrowing("pc()")
    public void afterThrowing(JoinPoint joinpoint){
        System.out.println("AfterThrowing: " +  joinpoint);
    }

    /**
     * 最終通知
     */
    @After("pc()")
    public void after(JoinPoint joinpoint){
        System.out.println("After: " +  joinpoint);
    }

    /**
     * 最終通知
     */
    @Around("pc()")
    public Object around(ProceedingJoinPoint pdj) throws Throwable {
        System.out.println("Around start: " + pdj);
        Object ret = pdj.proceed();
        System.out.println("Around end: " + pdj);
        return ret;
    }

    public static void main(String[] args) {
        Service1 target = new Service1();
        AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
        proxyFactory.setTarget(target);
        // 添加切面
        proxyFactory.addAspect(CommonCaseAspect.class);
        Service1 proxy = proxyFactory.getProxy();
        // 方法呼叫
        proxy.hello("xx");
        System.out.println("\n執行例外的結果:");
        proxy.throwException();
    }
}

正常執行方法的結果,不會觸發 @AfterThrowing 通知

Around start: execution(void com.crab.spring.aop.demo03.advice.Service1.hello(String))
Before: execution(void com.crab.spring.aop.demo03.advice.Service1.hello(String))
hello xx
AfterReturning: execution(void com.crab.spring.aop.demo03.advice.Service1.hello(String))
After: execution(void com.crab.spring.aop.demo03.advice.Service1.hello(String))
Around end: execution(void com.crab.spring.aop.demo03.advice.Service1.hello(String)

方法執行例外的結果,不會觸發@AfterReturning通知

執行例外的結果:
Around start: execution(void com.crab.spring.aop.demo03.advice.Service1.throwException())
Before: execution(void com.crab.spring.aop.demo03.advice.Service1.throwException())
throws a runtime exception
AfterThrowing: execution(void com.crab.spring.aop.demo03.advice.Service1.throwException())
After: execution(void com.crab.spring.aop.demo03.advice.Service1.throwException())
Exception in thread "main" java.lang.RuntimeException: 方法執行例外了

@Before

前置通知比較簡單,不深入講解,

@After

最終通知比較簡單,不深入講解,

@AfterReturning

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AfterReturning {
    String pointcut() default "";
	// 回傳值名稱
    String returning() default "";
}

可以通過returning指定注入的回傳值的名稱,需要注意的是通知方法中的回傳值型別,只有回傳型別和指定的型別或其子型別一致時,通知方法才會執行,使用Object可接收所有回傳型別,

案例說明

定義目標物件3個方法,回傳值型別分別是String、Long、void,

public class Service2 {
    public String getString() {
        System.out.println("Service2 getString");
        return "xxx";
    }

    public Long getLong() {
        System.out.println("Service2 getLong");
        return 100L;
    }

    public void m() {
        System.out.println("Service2 m");
    }
}

通知和測驗方法

@Aspect
public class AfterReturningAdvice {
    /**
     *
     */
    @Pointcut("execution(* com.crab.spring.aop.demo03.advice.Service2.*(..))")
    public void pc(){

    }

    /**
     * 回傳通知通過獲取returning回傳值名稱,
     * 注意方法中的第二個引數的型別,僅回傳指定型別的值的方法才會增強
     */
    @AfterReturning(pointcut = "pc()", returning = "retVal")
    public void afterReturning1(JoinPoint joinpoint, Object retVal) {
        System.out.println("AfterReturning  回傳 Object :  " + retVal);
    }

    /**
     * 回傳通知通過獲取returning回傳值名稱,
     * 注意方法中的第二個引數的型別,僅回傳指定型別String的值的方法才會增強
     */
    @AfterReturning(pointcut = "pc()", returning = "retVal")
    public void afterReturning1(JoinPoint joinpoint, String retVal) {
        System.out.println("AfterReturning  回傳 String :" + retVal);
    }

    public static void main(String[] args) {
        Service2 target = new Service2();
        AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
        proxyFactory.setTarget(target);
        proxyFactory.addAspect(AfterReturningAdvice.class);

        Service2 service2 = proxyFactory.getProxy();

        service2.getString();
        service2.getLong();
        service2.m();
    }

}

觀察下測驗結果

Service2 getString
AfterReturning  回傳 Object :  xxx
AfterReturning  回傳 String :xxx
Service2 getLong
AfterReturning  回傳 Object :  100
Service2 m
AfterReturning  回傳 Object :  null

afterReturning1 只攔截回傳值為String的方法getString()的執行,

`afterReturning2 攔截所有方法的執行,

@AfterThrowing

public @interface AfterThrowing {
    String pointcut() default "";
	// 指定拋出的例外引數名稱
    String throwing() default "";

throwing可以指定注入到通知方法中的例外引數的名稱,同時例外引數的型別會限制方法匹配,只有回傳指定例外型別或是其子型別才會執行增強方法,java.lang.Throwable匹配所有例外型別,

直接看下案例

public class Service3 {
    public void m(){
        throw new IllegalStateException("自定義拋出IllegalStateException");
    }

    public void m2(){
        throw new RuntimeException("自定義拋出RuntimeException");
    }
}

@Aspect
public class AfterThrowingAdvice {

    /**
     *
     */
    @Pointcut("execution(* com.crab.spring.aop.demo03.advice.Service3.*(..))")
    public void pc(){

    }

    /**
     * throwing指定例外引數名稱
     * 匹配 IllegalStateException
     * @param joinpoint
     * @param ex
     */
    @AfterThrowing(pointcut = "pc()", throwing = "ex")
    public void afterThrowing1(JoinPoint joinpoint, IllegalStateException ex) {
        System.out.println("AfterThrowing  例外型別 :  " + ex);
    }

    @AfterThrowing(pointcut = "pc()", throwing = "ex")
    public void afterThrowing2(JoinPoint joinpoint, Throwable ex) {
        System.out.println("AfterThrowing  例外型別 :  " + ex);
    }


    public static void main(String[] args) {
        Service3 target = new Service3();
        AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
        proxyFactory.setTarget(target);
        proxyFactory.addAspect(AfterThrowingAdvice.class);

        Service3 service3 = proxyFactory.getProxy();
        // service3.m();
        service3.m2();
    }

}

觀察下 service3.m()的輸出結果,2個攔截通知都執行了,

AfterThrowing  例外型別 :  java.lang.IllegalStateException: 自定義拋出IllegalStateException
AfterThrowing  例外型別 :  java.lang.IllegalStateException: 自定義拋出IllegalStateException
Exception in thread "main" java.lang.IllegalStateException: 自定義拋出IllegalStateException
	at com.crab.spring.aop.demo03.advice.Service3.m(Service3.java:11)

觀察下service3.m2();輸出結果,只有afterThrowing1沒有匹配到故不執行通知,

AfterThrowing  例外型別 :  java.lang.RuntimeException: 自定義拋出RuntimeException
Exception in thread "main" java.lang.RuntimeException: 自定義拋出RuntimeException

@Around

使用 @Around 注釋宣告環繞通知,通知方法的第一個引數必須是 ProceedingJoinPoint 型別,在通知方法體中,對 ProceedingJoinPoint 呼叫proceed() 會導致底層目標方法運行,

常用的場景是方法的統計耗時,或是快取層攔截方法的執行,直接回傳快取的資料,而不執行目標方法,

案例 統計耗時
@Aspect
public class AroundAdvice {
    static class MyService {
        public String getVal(String name) {
            System.out.println("MyService getVal");
            return UUID.randomUUID().toString();
        }

    }


    /**
     *
     */
    @Pointcut("execution(* com.crab.spring.aop.demo03.advice.AroundAdvice.MyService.*(..))")
    public void pc() {

    }

    @Around("pc()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("統計耗時開始");
        long start = System.nanoTime(); //統計耗時開始
        Object retVal = joinPoint.proceed();
        long end = System.nanoTime();
        System.out.println("統計耗時結束");
        System.out.println("方法執行耗時納秒:" + (end - start));
        return retVal;
    }

    public static void main(String[] args) {
        MyService target = new MyService();
        AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
        proxyFactory.setTarget(target);
        proxyFactory.addAspect(AroundAdvice.class);

        MyService service2 = proxyFactory.getProxy();

        service2.getVal("xx");
    }

}

觀察下輸出結果

統計耗時開始
MyService getVal
統計耗時結束
方法執行耗時納秒:87827000	

通知引數

JoinPoint 介面獲取資訊

上面的例子中,任何通知方法都可以宣告 org.aspectj.lang.JoinPoint 型別的引數作為其第一個引數,當然環繞通知第一個引數型別是ProceedingJoinPoint,它是 JoinPoint 的子類,

JoinPoint 提供了一些方法來提供對連接點可用狀態和關于連接點的靜態資訊的反射訪問,其主要原始碼如下,

public interface JoinPoint {
    // 列印所有通知方法的有用描述
    String toString();
	// 獲取代理物件
    Object getThis();
	// 獲取目標物件
    Object getTarget();
	// 獲取所有的方法引數
    Object[] getArgs();
    // 回傳被增強的方法的描述
    Signature getSignature();
}
args傳參

之前@AfterReturning可通過retVal將方法結果當做引數傳遞給通知方法,@AfterThrowing可通過throwing將拋出的例外當做引數傳遞給通知方法,切點運算式args也可以傳遞引數給通知方法,如果在 args 運算式中使用引數名稱代替型別名稱,則在呼叫通知時,相應引數的值將作為引數值傳遞,來看一個案例來理解,

定義一個引數物件

public class Account {
    private String name;
    private String password;
    // ...
}

使用args指定引數名

@Aspect
public class UseArgs {
    static class MyAccountService {
        public void validateAccount(Account account) {
            System.out.println("校驗Account :" + account);
        }
    }

    /**
     *
     */
    @Pointcut("execution(* com.crab.spring.aop.demo03.advice.parameter.UseArgs.MyAccountService.*(..))")
    public void pc(){

    }

    /**
     * args運算式不再指定引數型別,而是指定了傳遞到通知方法中的引數名稱,引數型別在通知方法中定義了
     * 此處指定了Account型別引數為account
     * @param account
     */
    @Before("pc() && args(account,..)")
    public void validateAccountBefore(JoinPoint joinPoint, Account account) {
        System.out.println("前置通知,傳遞的account引數: " + account);
    }

    public static void main(String[] args) {
        MyAccountService target = new MyAccountService();
        AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
        proxyFactory.setTarget(target);
        proxyFactory.addAspect(UseArgs.class);
        MyAccountService proxy = proxyFactory.getProxy();
        proxy.validateAccount(new Account("xx", "oo"));
    }

}

觀察下結果

前置通知,傳遞的account引數: Account{name='xx', password='oo'}
校驗Account :Account{name='xx', password='oo'}

在前置通知方法中,已經可以獲取到通過args傳遞的引數了,

@annotaion 傳參

類似args運算式進行型別匹配可傳遞引數,@annotaion匹配模板方法的的注解型別也可以以同樣的的方式進行傳參,通過案例了解下,

自定義注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Auditable {
    int value();
}

@annotation不指定注解型別,而是指定引數名稱

@Aspect
public class UseAnnotation {
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    @interface Auditable {
        int value();
    }

    static class MyAccountService {
        @Auditable(100)
        public void validateAccount(Account account) {
            System.out.println("校驗Account :" + account);
        }
    }

    /**
     *
     */
    @Pointcut("execution(* com.crab.spring.aop.demo03.advice.parameter.UseAnnotation.MyAccountService.*(..))")
    public void pc() {

    }

    /**
     * @annotation運算式不再指定目標方法包含的注解型別,而是指定了傳遞到通知方法中的引數名稱,引數型別在通知方法中定義了
     * 此處指定了auditable引數,型別是注解 Auditable
     *
     * @param auditable
     */
    @Before("pc() && @annotation(auditable)")
    public void validateAccountBefore(JoinPoint joinPoint, Auditable auditable) {
        System.out.println("前置通知,@annotation傳遞的auditable引數: " + auditable);
    }

    public static void main(String[] args) {
        MyAccountService target = new MyAccountService();
        AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
        proxyFactory.setTarget(target);
        proxyFactory.addAspect(UseAnnotation.class);
        MyAccountService proxy = proxyFactory.getProxy();
        proxy.validateAccount(new Account("xx", "oo"));
    }

}

觀察下輸出結果,通知方法可以獲取到作為引數傳遞的注解了,

前置通知,@annotation傳遞的auditable引數: @com.crab.spring.aop.demo03.advice.parameter.UseAnnotation$Auditable(value=https://www.cnblogs.com/kongbubihai/p/100)
校驗Account :Account{name='xx', password='oo'}

擴展一下:其它的匹配型別的切點運算式都可以通過類似的方法進行傳遞引數:

  • this 代理物件
  • target目標物件
  • @within
  • @target
  • @args
傳遞泛型引數

支持傳遞泛型引數,單值指定具體型別生效,泛型集合傳遞無效,看下案例

定義一個泛型介面

public interface Sample<T> {
    void sampleGenericMethod(T param);
    void sampleGenericCollectionMethod(Collection<T> params);
}

來一個Account的具體型別的實作類

    static class MyAccountService implements Sample<Account> {
        public void validateAccount(Account account) {
            System.out.println("校驗Account :" + account);
        }

        @Override
        public void sampleGenericMethod(Account param) {
            System.out.println("MyAccountService sampleGenericMethod :" + param);
        }

        @Override
        public void sampleGenericCollectionMethod(Collection<Account> params) {
            System.out.println("sampleGenericCollectionMethod: ");
            params.forEach(System.out::println);
        }
    }

通過args傳遞泛型引數和泛型集合引數

@Aspect
public class UseArgsGeneric {

    static class MyAccountService implements Sample<Account> {
        public void validateAccount(Account account) {
            System.out.println("校驗Account :" + account);
        }

        @Override
        public void sampleGenericMethod(Account param) {
            System.out.println("MyAccountService sampleGenericMethod :" + param);
        }

        @Override
        public void sampleGenericCollectionMethod(Collection<Account> params) {
            System.out.println("sampleGenericCollectionMethod: ");
        }
    }

    /**
     * 匹配 Sample介面及其子類的sampleGenericMethod方法執行
     */
    @Pointcut("execution(* com.crab.spring.aop.demo03.advice.parameter.Sample+.sampleGenericMethod(..))")
    public void pc() {

    }

    /**
     * 匹配 Sample介面及其子類的 sampleGenericCollectionMethod 方法執行
     */
    @Pointcut("execution(* com.crab.spring.aop.demo03.advice.parameter.Sample+.sampleGenericCollectionMethod(..))")
    public void pc2() {

    }

    /**
     * args 傳遞泛型引數,引數型別指定具體的型別Account
     *
     *
     * @param account
     */
    @Before("pc() && args(account,..)")
    public void before1(JoinPoint joinPoint, Account account) {
        System.out.println("前置通知,傳遞的account引數: " + account);
    }

    /**
     * args 傳遞泛型引數,引數型別指定具體的型別String
     *
     * @param account
     */
    @Before("pc() && args(account,..)")
    public void before2(JoinPoint joinPoint, String account) {
        System.out.println("前置通知,傳遞的account引數: " + account);
    }


    /**
     * 泛型集合無效
     * @param col
     */
    @Before("pc() && args(col,..)")
    public void before3(JoinPoint joinPoint, Collection<?> col) {
        System.out.println("前置通知,傳遞的集合引數: " + col);
    }

    public static void main(String[] args) {
        MyAccountService target = new MyAccountService();
        AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
        proxyFactory.setTarget(target);
        proxyFactory.addAspect(UseArgsGeneric.class);
        MyAccountService proxy = proxyFactory.getProxy();
        proxy.sampleGenericMethod(new Account("xx", "oo"));
        // before1將攔截

        // 觀察下集合形式
        List<Account> accountList = Arrays.asList(new Account("xx1", "00x"), new Account("00", "xx"), null);
        proxy.sampleGenericCollectionMethod(accountList);
    }

}

結果如下

前置通知,傳遞的account引數: Account{name='xx', password='oo'}
MyAccountService sampleGenericMethod :Account{name='xx', password='oo'}
sampleGenericCollectionMethod: 

單值的具體型別引數成功傳遞,而泛型集合無效,

通知順序

采用結論先上,驗證程式后行,最后原始碼分析收尾順序來說明,

同一個切面內不同通知型別的順序

image-20220207180452497

  1. 方法正常執行通知順序

    Around前操作
    Before:
    目標方法執行
    AfterReturning
    After
    
    
  2. 方法拋出例外退出通知順序

    Around前操作
    Before
    目標方法執行
    AfterThrowing
    After
    

測驗程式如下,含5種通知

package com.crab.spring.aop.demo03.advice.ordering;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;

/**
 * 同一個切面內不同通知型別的順序
 * @author zfd
 * @version v1.0
 * @date 2022/2/7 11:34
 * @關于我 請關注公眾號 螃蟹的Java筆記 獲取更多技術系列
 */
@Aspect // 切面
public class CommonAspect {

    /**
     * 公共切點
     *  匹配Service1的所有方法
     */
    @Pointcut("execution(* com.crab.spring.aop.demo03.advice.ordering.Service1.*(..))")
    public void pc(){

    }

    /**
     * 前置通知
     */
    @Before("pc()")
    public void before(JoinPoint joinpoint){
        System.out.println("Before: " +  joinpoint);
    }

    /**
     * 回傳通知
     */
    @AfterReturning("pc()")
    public void afterReturning(JoinPoint joinpoint){
        System.out.println("AfterReturning: " +  joinpoint);
    }

    /**
     * 拋出例外通知
     */
    @AfterThrowing("pc()")
    public void afterThrowing(JoinPoint joinpoint){
        System.out.println("AfterThrowing: " +  joinpoint);
    }

    /**
     * 最終通知
     */
    @After("pc()")
    public void after(JoinPoint joinpoint){
        System.out.println("After: " +  joinpoint);
    }

    /**
     * 最終通知
     */
    @Around("pc()")
    public Object around(ProceedingJoinPoint pdj) throws Throwable {
        System.out.println("Around start: " + pdj);
        Object ret = pdj.proceed();
        System.out.println("Around end: " + pdj);
        return ret;
    }

    public static void main(String[] args) {
        Service1 target = new Service1();
        AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
        proxyFactory.setTarget(target);
        // 添加切面
        proxyFactory.addAspect(CommonAspect.class);
        Service1 proxy = proxyFactory.getProxy();
        // 方法呼叫
        proxy.hello("xx");
        System.out.println("\n執行例外的結果:");
        proxy.throwException();
    }
}

觀察下輸出結果,符合結論

Around start: execution(String com.crab.spring.aop.demo03.advice.ordering.Service1.hello(String))
Before: execution(String com.crab.spring.aop.demo03.advice.ordering.Service1.hello(String))
hello xx
AfterReturning: execution(String com.crab.spring.aop.demo03.advice.ordering.Service1.hello(String))
After: execution(String com.crab.spring.aop.demo03.advice.ordering.Service1.hello(String))
Around end: execution(String com.crab.spring.aop.demo03.advice.ordering.Service1.hello(String))

執行例外的結果:
Around start: execution(void com.crab.spring.aop.demo03.advice.ordering.Service1.throwException())
Before: execution(void com.crab.spring.aop.demo03.advice.ordering.Service1.throwException())
throws a runtime exception
AfterThrowing: execution(void com.crab.spring.aop.demo03.advice.ordering.Service1.throwException())
After: execution(void com.crab.spring.aop.demo03.advice.ordering.Service1.throwException())
Exception in thread "main" java.lang.RuntimeException: 方法執行例外了

多個切面之間的通知順序

  1. 切面級別的優先級可以通過注解@Order或是實作介面org.springframework.core.Ordered,數值越小優先級越高,
  2. 類似洋蔥圈,前置方向的優先級越高,后置方向的優先級越低,

img

結論如下(以2個切面為例)

image-20220207182322550

  1. 方法正常執行

    Around1 start
    Before1
    Around2 start
    Before2
    目標方法執行
    AfterReturning2
    After2
    Around2 end
    AfterReturning
    Afte
    Around end
    
  2. 方法例外退出

    執行例外的結果:
    Around start
    Before
    Around2 start
    Before2
    目標方法執行并拋出例外
    AfterThrowing2
    After2
    AfterThrowing
    After
    

通知順序原始碼分析

之前的2篇原始碼分析從物件生成和代理方法執行流程分析得比較清晰了,一篇是使用編程式的AOP代理,另外一篇是使用@Aspect宣告式AOP代理,所以這里的原始碼分析著重點是在第二篇的基礎上看下注解式的通知方法是如何轉換成有序的Advios鏈,再到有序的MethodInterceptor鏈,如何執行的,

BeanPostProcessor觸發代理物件的生成

之前的分析說過Spring中AOP代理的物件的通過AbstractAutoProxyCreator這個BeanPostProcessor生成的,就已這個為切入點,看一下關鍵方法,

AbstractAutoProxyCreator#postProcessAfterInitialization方法

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
   if (bean != null) {
      Object cacheKey = getCacheKey(bean.getClass(), beanName);
      if (this.earlyProxyReferences.remove(cacheKey) != bean) {
          //  bean初始化后為需要代理的bean的創建代理物件,
         return wrapIfNecessary(bean, beanName, cacheKey);
      }
   }
   return bean;
}

``AbstractAutoProxyCreator#wrapIfNecessary`方法

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    // 1、 前面是判斷是否需要新創建代理物件
   if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
      return bean;
   }
   if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
      return bean;
   }
   if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
      this.advisedBeans.put(cacheKey, Boolean.FALSE);
      return bean;
   }

   // Create proxy if we have advice.
    // 2、獲取所有用于增強當前bean的Advisor鏈
   Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
   if (specificInterceptors != DO_NOT_PROXY) {
      this.advisedBeans.put(cacheKey, Boolean.TRUE);
       // 3、創建AOP代理物件
      Object proxy = createProxy(
            bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
      this.proxyTypes.put(cacheKey, proxy.getClass());
      return proxy;
   }

   this.advisedBeans.put(cacheKey, Boolean.FALSE);
   return bean;
}

保持耐心,繼續往下看下Advisor鏈的獲取,

獲取所有用于增強當前bean的Advisor鏈

子類AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean方法獲取所有用于增強當前bean的Advisor鏈

	protected Object[] getAdvicesAndAdvisorsForBean(
			Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
  		// 1、查找符合的Advisor鏈
		List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
		if (advisors.isEmpty()) {
			return DO_NOT_PROXY;
		}
		return advisors.toArray();
	}

AbstractAdvisorAutoProxyCreator#findEligibleAdvisors方法查找符合的Advisor鏈

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
	// 1、獲取候選的Advisos,實際呼叫其子類的方法實作,InstantiationModelAwarePointcutAdvisorImpl物件串列
   List<Advisor> candidateAdvisors = findCandidateAdvisors();
   //  2、進行篩選Advisor
   List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
   //  3、添加特殊的 ExposeInvocationInterceptor.ADVISOR 是DefaultPointcutAdvisor物件
   extendAdvisors(eligibleAdvisors);
   if (!eligibleAdvisors.isEmpty()) {
   	  // 4、關鍵的對Advisor進行排序
      eligibleAdvisors = sortAdvisors(eligibleAdvisors);
   }
   return eligibleAdvisors;
}

候選的Advisors主要是子類AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors方法實作,

@Override
protected List<Advisor> findCandidateAdvisors() {
    // 父類查找Advisor
   List<Advisor> advisors = super.findCandidateAdvisors();
   // Build Advisors for all AspectJ aspects in the bean factory.
    // 從容器中生成 Advisors
   if (this.aspectJAdvisorsBuilder != null) {
      advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
   }
   return advisors;
}

往下看如何從從容器中生成 Advisors,

容器中切面類中的通知如何生成Advisor

BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors()從切面生成Advisor

public List<Advisor> buildAspectJAdvisors() {
    //1、 保存已經找到的切面bean名稱,防止每一次都回圈一遍所有bean
   List<String> aspectNames = this.aspectBeanNames;
	
    // PS: 是否想起了單例模式的雙重檢查?
    // 決議切面beanName并快取,同時生成Adivsor
   if (aspectNames == null) {
      synchronized (this) {
         aspectNames = this.aspectBeanNames;
         if (aspectNames == null) {
            List<Advisor> advisors = new ArrayList<>();
            aspectNames = new ArrayList<>();
            String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                  this.beanFactory, Object.class, true, false);
             // 2、 回圈容器中的beanNames串列
            for (String beanName : beanNames) {
               if (!isEligibleBean(beanName)) {
                  continue;
               }
               Class<?> beanType = this.beanFactory.getType(beanName, false);
               if (beanType == null) {
                  continue;
               }
                // 3、 bean上面有Aspect就是切面
               if (this.advisorFactory.isAspect(beanType)) {
                   //  添加切面bean快取
                  aspectNames.add(beanName);
                  AspectMetadata amd = new AspectMetadata(beanType, beanName);
                  if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                     MetadataAwareAspectInstanceFactory factory =
                           new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);			// 4、關鍵的單個切面的所有通知轉換成Advisor鏈
                     List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
                     if (this.beanFactory.isSingleton(beanName)) {
                        this.advisorsCache.put(beanName, classAdvisors);
                     }
                     else {
                        this.aspectFactoryCache.put(beanName, factory);
                     }
                     advisors.addAll(classAdvisors);
                  }
                  else {
                     // Per target or per this.
                     if (this.beanFactory.isSingleton(beanName)) {
                        throw new IllegalArgumentException("Bean with name '" + beanName +
                              "' is a singleton, but aspect instantiation model is not singleton");
                     }
                     MetadataAwareAspectInstanceFactory factory =
                           new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
                     this.aspectFactoryCache.put(beanName, factory);
                      // 4、關鍵的單個切面的所有通知轉換成Advisor鏈
                     advisors.addAll(this.advisorFactory.getAdvisors(factory));
                  }
               }
            }
            this.aspectBeanNames = aspectNames;
            return advisors;
         }
      }
   }

   if (aspectNames.isEmpty()) {
      return Collections.emptyList();
   }
    // 快取過切面beanName 直接生成Advisor
   List<Advisor> advisors = new ArrayList<>();
   for (String aspectName : aspectNames) {
      List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
      if (cachedAdvisors != null) {
         advisors.addAll(cachedAdvisors);
      }
      else {
         MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
         advisors.addAll(this.advisorFactory.getAdvisors(factory));
      }
   }
   return advisors;
}

單個切面的通知方法是轉換成有序的Advisor鏈的?接著往下看,

單個切面類內的通知如何轉成有序的Advisor

ReflectiveAspectJAdvisorFactory#getAdvisors方法獲取Advisor

@Override
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
   Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
   String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
   validate(aspectClass);

   MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
         new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);

   List<Advisor> advisors = new ArrayList<>();
    // 1、getAdvisorMethods 獲取@Aspect切面中所有通知方法
   for (Method method : getAdvisorMethods(aspectClass)) {
       // 2、getAdvisor轉換成Advisor物件,InstantiationModelAwarePointcutAdvisorImpl
      Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
      if (advisor != null) {
          // 3、添加到 Advisor鏈
         advisors.add(advisor);
      }
   }
    // ...
   return advisors;
}

ReflectiveAspectJAdvisorFactory#getAdvisorMethods方法,獲取所有通知方法

private List<Method> getAdvisorMethods(Class<?> aspectClass) {
   final List<Method> methods = new ArrayList<>();
    // 1、獲取所有通知方法
   ReflectionUtils.doWithMethods(aspectClass, method -> {
      // Exclude pointcuts
      if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {
         methods.add(method);
      }
   }, ReflectionUtils.USER_DECLARED_METHODS);
   if (methods.size() > 1) {
       // 2、關鍵將通知方法排序的排序器
      methods.sort(METHOD_COMPARATOR);
   }
   return methods;
}

通知方法是如何包裝成MethodInterceptor的再到Advisor?

ReflectiveAspectJAdvisorFactory#getAdvisor()方法

	public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
			int declarationOrderInAspect, String aspectName) {

		validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());

		AspectJExpressionPointcut expressionPointcut = getPointcut(
				candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
		if (expressionPointcut == null) {
			return null;
		}
		// 此處轉換成 InstantiationModelAwarePointcutAdvisorImpl,里面將通知方法轉換成具體通知
		return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
				this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
	}

InstantiationModelAwarePointcutAdvisorImpl實作了Advisor介面

public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut,
      Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory,
      MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
	// 省略...
   else {
      // A singleton aspect.
      this.pointcut = this.declaredPointcut;
      this.lazy = false;
       // 將通知方法實體化為通知攔截器MethodInterceptor類
      this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
   }
}

看下InstantiationModelAwarePointcutAdvisorImpl#instantiateAdvice方法

private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) {
    // 干活的是AspectJAdvisorFactory
   Advice advice = this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut,
         this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
   return (advice != null ? advice : EMPTY_ADVICE);
}

看下干活的ReflectiveAspectJAdvisorFactory#getAdvice()方法是如何將通知方法實體化為通知攔截器MethodInterceptor類的,

public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
      MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {

   Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
   validate(candidateAspectClass);

   AspectJAnnotation<?> aspectJAnnotation =
         AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
   if (aspectJAnnotation == null) {
      return null;
   }

   AbstractAspectJAdvice springAdvice;
	// 1、獲取注解
   switch (aspectJAnnotation.getAnnotationType()) {
      case AtPointcut:
         if (logger.isDebugEnabled()) {
            logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
         }
         return null;
           // @Around -> AspectJAroundAdvice
      case AtAround:
         springAdvice = new AspectJAroundAdvice(
               candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
         break;
            // @Before -> AspectJMethodBeforeAdvice
      case AtBefore:
         springAdvice = new AspectJMethodBeforeAdvice(
               candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
         break;
            // @After -> AspectJAfterAdvice
      case AtAfter:
         springAdvice = new AspectJAfterAdvice(
               candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
         break;
            // @AfterReturning -> AspectJAfterReturningAdvice
      case AtAfterReturning:
         springAdvice = new AspectJAfterReturningAdvice(
               candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
         AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
         if (StringUtils.hasText(afterReturningAnnotation.returning())) {
            springAdvice.setReturningName(afterReturningAnnotation.returning());
         }
         break;
            // @AfterThrowing -> AspectJAfterThrowingAdvice
      case AtAfterThrowing:
         springAdvice = new AspectJAfterThrowingAdvice(
               candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
         AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
         if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
            springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
         }
         break;
      default:
         throw new UnsupportedOperationException(
               "Unsupported advice type on method: " + candidateAdviceMethod);
   }

   // Now to configure the advice...
   springAdvice.setAspectName(aspectName);
   springAdvice.setDeclarationOrder(declarationOrder);
   String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
   if (argNames != null) {
      springAdvice.setArgumentNamesFromStringArray(argNames);
   }
   springAdvice.calculateArgumentBindings();

   return springAdvice;
}

所以注解通知方法會轉成對應的通知類,對應關系如下:

@Around -> AspectJAroundAdvice

@Before -> AspectJMethodBeforeAdvice

@After -> AspectJAfterAdvice

@AfterReturning -> AspectJAfterReturningAdvice

@AfterThrowing -> AspectJAfterThrowingAdvice

接下是切面內的Advisor對應的方法的排序,關鍵的METHOD_COMPARATOR通知排序器

private static final Comparator<Method> METHOD_COMPARATOR;

static {
   // Note: although @After is ordered before @AfterReturning and @AfterThrowing,
   // an @After advice method will actually be invoked after @AfterReturning and
   // @AfterThrowing methods due to the fact that AspectJAfterAdvice.invoke(MethodInvocation)
   // invokes proceed() in a `try` block and only invokes the @After advice method
   // in a corresponding `finally` block.
   Comparator<Method> adviceKindComparator = new ConvertingComparator<>(
         new InstanceComparator<>(
             // 此處是通知方法的排序
               Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class),
         (Converter<Method, Annotation>) method -> {
            AspectJAnnotation<?> ann = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method);
            return (ann != null ? ann.getAnnotation() : null);
         });
   Comparator<Method> methodNameComparator = new ConvertingComparator<>(Method::getName);
   METHOD_COMPARATOR = adviceKindComparator.thenComparing(methodNameComparator);
}

單個切面的通知轉換成有序的Advisors了,回圈多個切面添加到統一的Adivisors鏈中,

此時是會發現:區域單個切面的Advisor有序,整體多個切面的所有Advisor是無序的,需要再來一次排序,

多個切面生成的Advisor鏈是如何排序的

回到AbstractAdvisorAutoProxyCreator#findEligibleAdvisors方法

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
	// 1、獲取候選的Advisos,實際呼叫其子類的方法實作
   List<Advisor> candidateAdvisors = findCandidateAdvisors();
    // 區域單個切面的Advisor有序,整體多個切面的所有Advisor是無序的,需要再來一次排序!
   //  2、進行篩選Advisor
   List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
   //  3、添加特殊的 ExposeInvocationInterceptor.ADVISOR 是DefaultPointcutAdvisor物件
   extendAdvisors(eligibleAdvisors);
   if (!eligibleAdvisors.isEmpty()) {
   	  // 4、關鍵的對Advisor進行排序
      eligibleAdvisors = sortAdvisors(eligibleAdvisors);
   }
   return eligibleAdvisors;
}

整體的Advisor鏈排序的職責是由其子類AspectJAwareAdvisorAutoProxyCreator#sortAdvisors方法實作的

protected List<Advisor> sortAdvisors(List<Advisor> advisors) {
    // 用holder包裝一下
   List<PartiallyComparableAdvisorHolder> partiallyComparableAdvisors = new ArrayList<>(advisors.size());
   for (Advisor advisor : advisors) {
      partiallyComparableAdvisors.add(
            new PartiallyComparableAdvisorHolder(advisor, DEFAULT_PRECEDENCE_COMPARATOR)); // 1 此處添加了一個排序器
   }
    // 2 使用排序器進行排序
   List<PartiallyComparableAdvisorHolder> sorted = PartialOrder.sort(partiallyComparableAdvisors);
   if (sorted != null) {
      List<Advisor> result = new ArrayList<>(advisors.size());
      for (PartiallyComparableAdvisorHolder pcAdvisor : sorted) {
          //3  將排序的后的advisor回傳
         result.add(pcAdvisor.getAdvisor());
      }
      return result;
   }
   else {
      return super.sortAdvisors(advisors);
   }
}

排序器DEFAULT_PRECEDENCE_COMPARATORAnnotationAwareOrderComparator物件

private static final Comparator<Advisor> DEFAULT_PRECEDENCE_COMPARATOR = new AspectJPrecedenceComparator();

/**
* Create a default {@code AspectJPrecedenceComparator}.
*/
public AspectJPrecedenceComparator() {
this.advisorComparator = AnnotationAwareOrderComparator.INSTANCE;
}

AnnotationAwareOrderComparator是Spring提供的一個排序器,用處非常廣泛,AnnotationAwareOrderComparatorOrderComparator的擴展,它支持Spring的org.springframework.core.Ordered介面以及@Order@Priority注解,

package org.springframework.core.annotation;

public class AnnotationAwareOrderComparator extends OrderComparator {

   // 實體
   public static final AnnotationAwareOrderComparator INSTANCE = new AnnotationAwareOrderComparator();


   /**
    * 除了超類中的org.springframework.core.Ordered檢查外,
    * 這個實作還檢查各種型別的元素的@Order或@Priority
    */
   @Override
   @Nullable
   protected Integer findOrder(Object obj) {
      Integer order = super.findOrder(obj);
      if (order != null) {
         return order;
      }
      return findOrderFromAnnotation(obj);
   }

    // 從 @Order或@Priority 獲取排序數值
   @Nullable
   private Integer findOrderFromAnnotation(Object obj) {
      AnnotatedElement element = (obj instanceof AnnotatedElement ? (AnnotatedElement) obj : obj.getClass());
      MergedAnnotations annotations = MergedAnnotations.from(element, SearchStrategy.TYPE_HIERARCHY);
      Integer order = OrderUtils.getOrderFromAnnotations(element, annotations);
      if (order == null && obj instanceof DecoratingProxy) {
         return findOrderFromAnnotation(((DecoratingProxy) obj).getDecoratedClass());
      }
      return order;
   }

   @Override
   @Nullable
   public Integer getPriority(Object obj) {
      if (obj instanceof Class) {
         return OrderUtils.getPriority((Class<?>) obj);
      }
      Integer priority = OrderUtils.getPriority(obj.getClass());
      if (priority == null  && obj instanceof DecoratingProxy) {
         return getPriority(((DecoratingProxy) obj).getDecoratedClass());
      }
      return priority;
   }
   public static void sort(List<?> list) {
      if (list.size() > 1) {
         list.sort(INSTANCE);
      }
   }

   public static void sort(Object[] array) {
      if (array.length > 1) {
         Arrays.sort(array, INSTANCE);
      }
   }

   public static void sortIfNecessary(Object value) {
      if (value instanceof Object[]) {
         sort((Object[]) value);
      }
      else if (value instanceof List) {
         sort((List<?>) value);
      }
   }

}

通過獲取切面類上的的org.springframework.core.Ordered介面或是@Order@Priority注解對應的排序值,排序后就可以得到整體優先級由高到低的有序的Advisor鏈,

舉個例子,假設Aspect1的優先級是1,Aspect2的優先級是2,那么最終的Advisor鏈如下,

ExposeInvocationInterceptor.ADVISOR(DefaultPointcutAdvisor)

Aspect1的:

(AspectJAroundAdvice) InstantiationModelAwarePointcutAdvisorImpl

(AspectJMethodBeforeAdvice) InstantiationModelAwarePointcutAdvisorImpl

(AspectJAfterAdvice) InstantiationModelAwarePointcutAdvisorImpl

(AspectJAfterReturningAdvice) InstantiationModelAwarePointcutAdvisorImpl

(AspectJAfterThrowingAdvice) InstantiationModelAwarePointcutAdvisorImpl

Aspect2的:

(AspectJAroundAdvice) InstantiationModelAwarePointcutAdvisorImpl

(AspectJMethodBeforeAdvice) InstantiationModelAwarePointcutAdvisorImpl

(AspectJAfterAdvice) InstantiationModelAwarePointcutAdvisorImpl

(AspectJAfterReturningAdvice) InstantiationModelAwarePointcutAdvisorImpl

(AspectJAfterThrowingAdvice) InstantiationModelAwarePointcutAdvisorImpl

MethodInterceptor鏈執行程序

此處的代理物件是通過CGLIB 方式創建的代理,所以從CglibAopProxy#getProxy()方法入手,

@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
   try {
      // ... 省略非關注的代碼
      // Configure CGLIB Enhancer...
      Enhancer enhancer = createEnhancer();
      enhancer.setSuperclass(proxySuperClass);
      enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
      enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
      enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));
	  // GGLIB的關鍵是回呼的設定
      Callback[] callbacks = getCallbacks(rootClass);
      Class<?>[] types = new Class<?>[callbacks.length];
      for (int x = 0; x < types.length; x++) {
         types[x] = callbacks[x].getClass();
      }
      // fixedInterceptorMap only populated at this point, after getCallbacks call above
      enhancer.setCallbackFilter(new ProxyCallbackFilter(
            this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
      enhancer.setCallbackTypes(types);

      // Generate the proxy class and create a proxy instance.
      return createProxyClassAndInstance(enhancer, callbacks);
   }
   
}

關鍵方法是回呼的設定

CglibAopProxy#getProxy()方法,關注關鍵的攔截器設定,洗掉了部分不關注代碼

private Callback[] getCallbacks(Class<?> rootClass) throws Exception {

   // Choose an "aop" interceptor (used for AOP calls).
    // AOP相關的攔截器設定 advised 就是AdvisedSupport物件,熟悉的代理配置類
   Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
	
   Callback[] mainCallbacks = new Callback[] {
         aopInterceptor,  // for normal advice
         targetInterceptor,  // invoke target without considering advice, if optimized
         new SerializableNoOp(),  // no override for methods mapped to this
         targetDispatcher, this.advisedDispatcher,
         new EqualsInterceptor(this.advised),
         new HashCodeInterceptor(this.advised)
   };

   Callback[] callbacks;
   callbacks = mainCallbacks;
   return callbacks;
}

上面將就是AdvisedSupport物件傳遞給了DynamicAdvisedInterceptor物件,DynamicAdvisedInterceptor應該不陌生,CGLIB的通用的AOP攔截器,代理方法的呼叫會觸發該攔截器的intercept方法,

DynamicAdvisedInterceptor#intercept方法

@Override
@Nullable
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
   Object oldProxy = null;
   boolean setProxyContext = false;
   Object target = null;
   TargetSource targetSource = this.advised.getTargetSource();
   try {
      if (this.advised.exposeProxy) {
         // Make invocation available if necessary.
         oldProxy = AopContext.setCurrentProxy(proxy);
         setProxyContext = true;
      }
      // Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
      target = targetSource.getTarget();
      Class<?> targetClass = (target != null ? target.getClass() : null);
       // 1、關鍵的從AdvisedSupport中的Advisor鏈獲取MethodInterceptor鏈
      List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
      Object retVal;
      // Check whether we only have one InvokerInterceptor: that is,
      // no real advice, but just reflective invocation of the target.
      if (chain.isEmpty() && CglibMethodInvocation.isMethodProxyCompatible(method)) {
         Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
         try {
            retVal = methodProxy.invoke(target, argsToUse);
         }
         catch (CodeGenerationException ex) {
            CglibMethodInvocation.logFastClassGenerationFailure(method);
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
         }
      }
      else {
         // We need to create a method invocation...
          // 2、包裝成 CglibMethodInvocation并執行proceed()
         retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
      }
      retVal = processReturnType(proxy, target, method, retVal);
      return retVal;
   }
   finally {
      if (target != null && !targetSource.isStatic()) {
         targetSource.releaseTarget(target);
      }
      if (setProxyContext) {
         // Restore old proxy.
         AopContext.setCurrentProxy(oldProxy);
      }
   }
}

上面的關鍵是方法:

  1. 關鍵的從AdvisedSupport中的Advisor鏈獲取MethodInterceptor鏈

    主要邏輯是之前分析過的使用適配DefaultAdvisorAdapterRegistry將Advisor中通知包裝成對應的MethodInterceptor類,不過此處注意的是Advisor中的部分通知在前面已經包裝成MehtodIntertor的子類物件了,此處就不需要適配轉換了,否則需要適配轉換如下

    @Around -> AspectJAroundAdvice
    @Before -> AspectJMethodBeforeAdvice-> MethodBeforeAdviceInterceptor
    @After -> AspectJAfterAdvice
    @AfterReturning -> AspectJAfterReturningAdvice->AfterReturningAdviceInterceptor
    @AfterThrowing -> AspectJAfterThrowingAdvice
    
  2. 包裝成 CglibMethodInvocation并執行proceed(),其實最終執行就是我們分析中的ReflectiveMethodInvocation.procced,將MehtodIntertor攔截器鏈有序地執行

    @Override
    @Nullable
    public Object proceed() throws Throwable {
       // We start with an index of -1 and increment early.
       if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
          return invokeJoinpoint();
       }
    	// 1 獲取攔截器鏈
       Object interceptorOrInterceptionAdvice =
             this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
      //  省略
       else {
    	 // 2 依次執行攔截器鏈
          return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
       }
    }
    

5個通知類決議

以單個切面內的通知順序開始著手除錯,直接將斷點打到org.springframework.aop.framework.ReflectiveMethodInvocation.proceed()

image-20220208093059121

可以看到將執行的攔截器依次如下:

ExposeInvocationInterceptor
ApectJAroundAdvice
MethodBeforeAdviceInterceptor
ApectJAfterAdvice
AfterReturningAdviceInterceptor
ApectJAfterThrowingAdvice

第一個之前說過是附加的特殊的攔截器,可以先忽略,來看下其它5個攔截器的類圖和對應的通知,

image-20220208094207275

AspectJAroundAdvice 類

注意觀察執行程序,后面的類類似

public class AspectJAroundAdvice extends AbstractAspectJAdvice implements MethodInterceptor, Serializable {

   public AspectJAroundAdvice(
         Method aspectJAroundAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {

      super(aspectJAroundAdviceMethod, pointcut, aif);
   }


   @Override
   public boolean isBeforeAdvice() {
      return false;
   }

   @Override
   public boolean isAfterAdvice() {
      return false;
   }

   @Override
   protected boolean supportsProceedingJoinPoint() {
      return true;
   }

   @Override
   public Object invoke(MethodInvocation mi) throws Throwable {
      if (!(mi instanceof ProxyMethodInvocation)) {
         throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
      }
      ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
      ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
      JoinPointMatch jpm = getJoinPointMatch(pmi);
       // 1 呼叫around的增強方法
      return invokeAdviceMethod(pjp, jpm, null, null);
   }
}
MethodBeforeAdviceInterceptor 類

注意觀察執行程序,后面的類類似

public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable {

   private final MethodBeforeAdvice advice;
   public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
      Assert.notNull(advice, "Advice must not be null");
      this.advice = advice;
   }

   @Override
   public Object invoke(MethodInvocation mi) throws Throwable {
       // 1 執行前置通知
      this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
       // 2 進入下一個攔截器
      return mi.proceed();
   }

}
AspectJAfterAdvice 類
public class AspectJAfterAdvice extends AbstractAspectJAdvice
      implements MethodInterceptor, AfterAdvice, Serializable {

   public AspectJAfterAdvice(
         Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
      super(aspectJBeforeAdviceMethod, pointcut, aif);
   }


   @Override
   public Object invoke(MethodInvocation mi) throws Throwable {
      try {
          // 1 先執行下一個攔截器鏈 在try-finally中
         return mi.proceed();
      }
      finally {
          // 2 呼叫最終通知方法
         invokeAdviceMethod(getJoinPointMatch(), null, null);
      }
   }
}
AfterReturningAdviceInterceptor 類
public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {

   private final AfterReturningAdvice advice;
   public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {
      Assert.notNull(advice, "Advice must not be null");
      this.advice = advice;
   }


   @Override
   public Object invoke(MethodInvocation mi) throws Throwable {
       //1 先執行下一個攔截器,等待回傳結果
      Object retVal = mi.proceed();
       // 2 后執行回傳通知
      this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
       // 3 回傳通知處理后的結果
      return retVal;
   }

}
AspectJAfterThrowingAdvice 類
public class AspectJAfterThrowingAdvice extends AbstractAspectJAdvice
      implements MethodInterceptor, AfterAdvice, Serializable {

   public AspectJAfterThrowingAdvice(
         Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {

      super(aspectJBeforeAdviceMethod, pointcut, aif);
   }


   @Override
   public boolean isBeforeAdvice() {
      return false;
   }

   @Override
   public boolean isAfterAdvice() {
      return true;
   }

   @Override
   public void setThrowingName(String name) {
      setThrowingNameNoCheck(name);
   }

   @Override
   public Object invoke(MethodInvocation mi) throws Throwable {
      try {
          // 1  先執行下一個通知鏈 在try-catch
         return mi.proceed();
      }
      catch (Throwable ex) {
         if (shouldInvokeOnThrowing(ex)) {
             // 2 后執行例外拋出后通知
            invokeAdviceMethod(getJoinPointMatch(), null, ex);
         }
         throw ex;
      }
   }

   /**
    * 通知型別是否和@AfterThrowing.throwing配置的通知型別匹配
    */
   private boolean shouldInvokeOnThrowing(Throwable ex) {
      return getDiscoveredThrowingType().isAssignableFrom(ex.getClass());
   }

}

圖解模擬

單個切面5個通知
  • 紫色:攔截器鏈自上而下遞回執行
  • 藍色:目標方法無例外,攔截器鏈自下而上遞回回傳
  • 紅色:目標方法有例外,攔截器鏈自下而上遞回回傳

image-20220209083229035

結合上面的圖和5個通知的invoke方法邏輯,偽代碼如下:

{
	Around start
	{
		Before invoke()
		{
			try{
				{
					{
						try{
							目標方法執行()
						}catche(Throwable ex){
							AfterThrowing.invoke()
						}
					}
					AfterReturning invoke()
				}
			} finally{
				After invoke()
			}
		}
	}
	Around end
}
2個切面10個通知

2個切面10個通知的執行圖解如下:

image-20220209084151279

結合上面的圖和5個通知的invoke方法邏輯,偽代碼如下:

{
	Around start
	{
		Before invoke()
		{
			try{
				{
					{
						try{
							// 目標方法執行() 此處套娃下一個切面的通知
							{
                                Around2 start
                                {
                                    Before2 invoke()
                                    {
                                        try{
                                            {
                                                {
                                                    try{
                                                        目標方法執行()
                                                    }catche(Throwable ex){
                                                       AfterThrowing2.invoke()
                                                    }
                                                }
                                                AfterReturning2 invoke()
                                            }
                                        } finally{
                                            After2 invoke()
                                        }
                                    }
                                }
                                Around2 end
                            }	
						}catche(Throwable ex){
							AfterThrowing.invoke()
						}
					}
					AfterReturning invoke()
				}
			} finally{
				After invoke()
			}
		}
	}
	Around end
}

總結

本文介紹如何宣告通知、如何傳遞引數到通知方法中、多種通知多個切面的通知順序原始碼分析與圖解,

本篇原始碼地址:https://github.com/kongxubihai/pdf-spring-series/blob/main/spring-series-aop/src/main/java/com/crab/spring/aop/demo03/advice
知識分享,轉載請注明出處,學無先后,達者為先!

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/445862.html

標籤:Java

上一篇:集合框架(Collection集合的功能測驗)

下一篇:Java的jstack命令使用詳解

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more