聊聊 Spring AOP 的不為常知的“秘事”


Spring AOP 在我們日常開發中扮演了一個非常重要的角色,對於如何使用 AOP 相信很多人已經不陌生,但其中有一些點卻容易被我們忽視,本節我們結合一些“不為常知”的問題展開討論。

同一個 AOP 類中幾個切面注解的執行順序

先給出結論:@Around [joinPoint.proceed()前] —> @Before —> @Around [joinPoint.proceed()以及之后] —> @After —> @AfterReturning(如果有異常則@AfterThrowing)

話不多說,我這們直接上代碼:

@Aspect
public class AopClass {

    @Pointcut(value = "execution(* io.alan.*.Klass.*dong(..))")
    public void point() {

    }

    @Before(value = "point()")
    public void before() {
        System.out.println("========>begin klass dong... //2");
    }

    @Around("point()")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("========>around begin klass dong //1");
        joinPoint.proceed();
        System.out.println("========>around after klass dong //3");
    }
  
    @After(value = "point()")
    public void after() {
        System.out.println("========>after klass dong... //4");
    }
  
    @AfterReturning(value = "point()")
    public void afterReturning() {
        System.out.println("========>after klass dong... //5");
    }

}

查看應用打印的日志,如下:

image

不同 AOP 類切面的執行順序

如果我們對同一個方法定義多個 AOP,它的執行順序是什么樣的呢?

配置 AOP 執行順序的三種方式

方式1:實現 org.springframework.core.Ordered 接口
@Aspect
public class AopClass implements Ordered {

    @Override
    public int getOrder() {
        return 2;
    }

}
方式2:使用注解 @Order
@Aspect
@Order(2)
public class AopClass {

}
方式3:使用配置文件
<aop:config expose-proxy="true">
	<aop:aspect ref="aopBean" order="2">  
		<aop:pointcut id="testPointcut"  expression="@annotation(xxx.xxx.xxx.annotation.xxx)"/>  
   		<aop:around pointcut-ref="testPointcut" method="doAround" />  
        </aop:aspect>  
</aop:config>

代碼測試

下面我們演示在同一份代碼上加上兩個 AOP,然后觀察日志。

@Aspect
@Order(2)
public class Aop2 {

    @Pointcut(value = "execution(* io.kimmking.*.Klass.*dong(..))")
    public void point() {

    }

    @Before(value = "point()")
    public void before() {
        System.out.println("    Aop2========>before klass dong... //2");
    }

    @AfterReturning(value = "point()")
    public void afterReturning() {
        System.out.println("    Aop2========>afterReturning klass dong... //5");
    }

    @After(value = "point()")
    public void after() {
        System.out.println("    Aop2========>after klass dong... //4");
    }

    @Around("point()")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("    Aop2========>around before klass dong //1");
        joinPoint.proceed();
        System.out.println("    Aop2========>around after klass dong //3");

    }

}
@Aspect
@Order(3)
public class Aop3 {

    @Pointcut(value = "execution(* io.kimmking.*.Klass.*dong(..))")
    public void point() {

    }

    @Before(value = "point()")
    public void before() {
        System.out.println("    Aop3========>before klass dong... //2");
    }

    @AfterReturning(value = "point()")
    public void afterReturning() {
        System.out.println("    Aop3========>afterReturning klass dong... //5");
    }

    @After(value = "point()")
    public void after() {
        System.out.println("    Aop3========>after klass dong... //4");
    }

    @Around("point()")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("    Aop3========>around before klass dong //1");
        joinPoint.proceed();
        System.out.println("    Aop3========>around after klass dong //3");

    }

}

打印的日志如下:

image

從日志中我們可以看到,order 越小的 AOP 類越先執行,但還有一點需要我們注意:就是最先執行的 AOP 最后才執行結束(如上圖中的 AOP2)。

我們通過下面的這張示意圖就可以理解了。

image

小結一下:我們可以將 Spring AOP 當作一個同心圓,要執行的方法為圓心,最外層的 order 最小。從最外層按照 AOP1、AOP2 的順序依次執行 Around 方法,Before 方法。然后執行 method 方法,最后按照 AOP2、AOP1 (順序反過來了)的順序依次執行 After、AfterReturn 方法。

所以對多個AOP來說,先執行 before 的,一定后執行 after。

例如,如果我們要在同一個方法事務提交后執行自己的 AOP ,那么可以把事務的 AOP order 設置為2,自己的 AOP order 設置為1,然后在 AfterReturn 里邊處理自己的業務邏輯。

參考鏈接


END


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM