聊聊 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
posted @ 2021-07-23 13:16  Alan-Yin  阅读(404)  评论(1编辑  收藏  举报