Spring AOP中增强Advice的执行顺序

打印 上一主题 下一主题

主题 582|帖子 582|积分 1746

Spring AOP中增强Advice的执行顺序

本文主要验证Spring AOP中Advice的执行顺序问题。(Spring版本: 5.3.23)
Spring AOP中Advice分类

Spring AOP中Advice可分为如下五类:

  • @Around
  • @Before
  • @AfterReturning
  • @AfterThrowing
  • @After
Advice相关概念参考
同一Apsect中不同类型Advice执行顺序

配置基础环境


  • 依赖版本


  • Spring 版本为: 5.3.23
  • Spring Boot 版本为: 2.6.12
  • aspectjweaver 版本: 1.9.9.1

  • 定义Spring Boot启动类
  1. package sakura.springinaction;
  2. @SpringBootApplication
  3. @EnableAspectJAutoProxy
  4. public class MySpringApplication {
  5.         public static void main(String[] args) {
  6.                 SpringApplication.run(MySpringApplication.class, args);
  7.         }
  8. }
复制代码

  • 定义一个用于测试的Controller类
  1. package sakura.springinaction.controller;
  2. @Controller
  3. @Slf4j
  4. public class IndexController {
  5.         @GetMapping("/time")
  6.         @ResponseBody
  7.         public String time() {
  8.                 LocalDateTime now = LocalDateTime.now();
  9.                 String nowTime = now.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
  10.                 log.info("Current time: " + nowTime);
  11.                 return nowTime;
  12.         }
  13. }
复制代码

  • 定义一个声明式切面 Apsect1
  1. @Slf4j
  2. @Component
  3. @Aspect
  4. public class Aspect1 {
  5.    // 定义 Point Cut 切面
  6.    @Pointcut("execution(public * sakura.springinaction.controller.*.*(..))")
  7.    public void controllerLayer() {
  8.    }
  9.    // 定义Advice
  10.    @Before("controllerLayer()")
  11.    private void beforeAdvice2() {
  12.            log.info("Aspect_1 # @Before");
  13.    }
  14.    @After("controllerLayer() && @annotation(getMapping)")
  15.    private void afterAdvice1(GetMapping getMapping) {
  16.            log.info("Aspect_1 # @afterAdvice" + " path: " + Arrays.toString(getMapping.value()));
  17.    }
  18.    @AfterReturning(pointcut = "controllerLayer()", returning = "val")
  19.    private void afterReturningAdvice(Object val) {
  20.            log.info("Aspect_1 # @AfterReturning" + " returnValue: " + val);
  21.    }
  22.    @AfterThrowing(pointcut = "controllerLayer()", throwing = "thrower")
  23.    private void afterThrowingAdvice(Throwable thrower) {
  24.            log.info("Aspect_1 # @AfterThrowing" + " thrower: " + thrower.getClass().getName());
  25.    }
  26.    @Around("controllerLayer() && @annotation(getMapping)")
  27.    private Object aroundAdvice(ProceedingJoinPoint pjp, GetMapping getMapping) throws Throwable {
  28.            // Around 前置处理
  29.            Stopwatch stopwatch = Stopwatch.createStarted();
  30.            log.info("Aspect_1 # @Around-Before" + " methodName: " + pjp.getSignature().getName() + ", path: " + Arrays.toString(getMapping.value()));
  31.            Object result = pjp.proceed();
  32.            // Around 后置处理
  33.            log.info("Aspect_1 # @Around-After" + " methodName: " + pjp.getSignature().getName() + ", runTime: " + stopwatch.elapsed(TimeUnit.NANOSECONDS));
  34.            return result;
  35.    }
  36. }
复制代码
实验结果

在 发起请求(http://localhost:8080/time) 后,日志输出如图:

结论

在同一个切面(Apsect)定义中对于同一个Join Point而言,不同类型的Advice执行先后顺序依次是:

  • @Around 前置处理
  • @Before
  • @AfterReturning/@AfterThrowing
  • @After
  • @Around 后置置处理
优先级说明:

  • 对于进入Join Point的Advice而言(比如: @Around 前置处理,@Before),优先级越高,越先执行;
  • 对于从Join Point出来的Advice而言(比如: @Around 后置处理,@After),优先级越高,越后执行;
  • 优先级从高到低依次为: @Around, @Before,@After,@AfterReturning,@AfterThrowing;
PS:
如果在同一个切面(Apsect)中定义了两个同类型的Advice(比如定义两个@Before), 对于某个Join Point而言这两个Advice都匹配,那么这两个Advice执行的先后顺序是无法确定的。
不同Aspect中Advice执行顺序

问: 当不同的Aspect中的Advice 都匹配到了同一个Join Point,那么那个Aspect中的Advice 先执行,那个后执行呢?
答: 不确定 ,但是可以通过在class上添加注解@Order指定优先级确定执行顺序(参考文档)
实验一: Aspect1为高优先级,Aspect2为低优先级


  • 与Aspect1 类似,再定义一个切面类Aspect2,如下
  1. package sakura.springinaction.advice;
  2. import org.springframework.core.annotation.Order;
  3. @Slf4j
  4. @Component
  5. @Aspect
  6. @Order(2)
  7. public class Aspect2 {
  8.   // 定义Advice
  9.   @Before("sakura.springinaction.advice.Aspect1.controllerLayer()")
  10.   private void beforeAdvice2() {
  11.     log.info("Aspect_2 # @Before");
  12.   }
  13.   @After("sakura.springinaction.advice.Aspect1.controllerLayer() && @annotation(getMapping)")
  14.   private void afterAdvice1(GetMapping getMapping) {
  15.     log.info("Aspect_2 # @afterAdvice" + " path: " + Arrays.toString(getMapping.value()));
  16.   }
  17.   @AfterReturning(pointcut = "sakura.springinaction.advice.Aspect1.controllerLayer()", returning = "val")
  18.   private void afterReturningAdvice(Object val) {
  19.     log.info("Aspect_2 # @AfterReturning" + " returnValue: " + val);
  20.   }
  21.   @AfterThrowing(pointcut = "sakura.springinaction.advice.Aspect1.controllerLayer()", throwing = "thrower")
  22.   private void afterThrowingAdvice(Throwable thrower) {
  23.     log.info("Aspect_2 # @AfterThrowing" + " thrower: " + thrower.getClass().getName());
  24.   }
  25.   @Around("sakura.springinaction.advice.Aspect1.controllerLayer() && @annotation(getMapping)")
  26.   private Object aroundAdvice(ProceedingJoinPoint pjp, GetMapping getMapping) throws Throwable {
  27.     Stopwatch stopwatch = Stopwatch.createStarted();
  28.     log.info("Aspect_2 # @Around-Before" + " methodName: " + pjp.getSignature().getName() + ", path: " + Arrays.toString(getMapping.value()));
  29.     Object result = pjp.proceed();
  30.     log.info("Aspect_2 # @Around-After" + " methodName: " + pjp.getSignature().getName() + ", runTime: " + stopwatch.elapsed(TimeUnit.NANOSECONDS));
  31.     return result;
  32.   }
  33. }
复制代码

  • Aspect1 添加@Order注解指定优先级,如下
  1. @Slf4j
  2. @Component
  3. @Aspect
  4. @Order(1)
  5. public class Aspect1 {
  6.   //...
  7. }
复制代码
此时,Aspect1的优先级比Aspect2的优先级高。
实验结果

实验结果如下:

说明:
高优先级的Aspect1中的@Around前置处理和@Before先于低优先级的Aspect2执行,而@AfterReturning,@After和@Around后置处理,则低优先级的Aspect2先执行。
实验二: Aspect1为低优先级,Aspect2为高优先级


  • 更改两个Aspect中@Order注解优先级,如下:
  1. @Slf4j
  2. @Component
  3. @Aspect
  4. @Order(2)
  5. public class Aspect1 {
  6.   //...
  7. }
复制代码
  1. @Slf4j
  2. @Component
  3. @Aspect
  4. @Order(1)
  5. public class Aspect2 {
  6.         //...
  7. }
复制代码
实验结果

实验结果如下:

结论


  • 当不同的Aspect中的Advice 都匹配到了同一个Join Point,不同Aspect中的Advice 执行顺序不确定。
  • 通过在Aspect类上添加注解@Order指定优先级,确定执行顺序,执行顺序满足如下规律

    • 对于@Around前置处理 和@Before两种Advice而言,所在的Aspect优先级越高,越先执行
    • 对于@AfterReturning,@AfterThrowing,@After和@Around后置处理 类型的Advice而言,所在的Aspect优先级越高,越后执行

参考资料:

本文主要目的是记录学习过程,加深对知识点理解; 如有行文有误,望指正。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

北冰洋以北

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表