Spring Boot 切面执行优先级配置指南

引言

在 Spring Boot 应用开发中,切面编程(AOP)是一项强大的功能,它允许我们将横切关注点(如日志记录、事务管理、权限控制等)从业务逻辑中分离出来,以提高代码的可维护性和可扩展性。当存在多个切面时,了解和控制切面的执行优先级顺序就显得尤为重要。本文将深入介绍 Spring Boot 框架中切面执行优先级顺序的使用方式。

切面优先级的概念

在 Spring AOP 中,切面的优先级决定了多个切面在目标方法执行前后的执行顺序。高优先级的切面会先于低优先级的切面执行。在环绕通知(@Around)中,高优先级切面的通知方法先进入,后退出;而在前置通知(@Before)中,高优先级切面的通知方法先执行,后置通知(@After)和返回后通知(@AfterReturning)以及异常通知(@AfterThrowing)则相反,低优先级切面的通知方法先执行。

优先级设置方式

@Order`注解

  • 作用‌:在切面类上标注 @Order,数值越小优先级越高‌。
  • 示例‌:
@Aspect  
@Component  
@Order(1) // 优先级高于 Order(2) 的切面  
public class LogAspect {  
    // 切面逻辑  
}  

  • 规则‌:
    • 未标注 @Order 的切面默认优先级最低(数值最大)‌。
    • 同一优先级下,切面执行顺序由类名或方法名的字典序决定‌。

实现 Ordered 接口

  • 作用‌:通过重写 getOrder()方法动态设置优先级‌
  • 示例‌:
@Aspect  
@Component  
public class ValidationAspect implements Ordered {  
    @Override  
    public int getOrder() {  
        return 2; // 优先级低于返回 1 的切面  
    }  
}  

执行顺序规则

同一连接点的多个切面

  • ‌前置通知(@Before):优先级高的切面先执行‌。
  • ‌后置通知(@After、@AfterReturning、@AfterThrowing):优先级高的切面后执行‌。
  • ‌环绕通知(@Around):优先级高的切面外层包裹优先级低的切面‌。

示例场景

假设切面 OrderDemoAspect1(优先级1), OrderDemoAspect2(优先级2),OrderDemoAspect3(优先级2)和 OrderDemoAspect4(默认优先级)作用于同一方法:

  1. OrderDemoAspect1的 @Around 开始
  2. OrderDemoAspect1的 @Before
  3. OrderDemoAspect2的 @Around 开始 →
  4. OrderDemoAspect2的 @Before
  5. OrderDemoAspect3的 @Around 开始 →
  6. OrderDemoAspect3的 @Before →
  7. OrderDemoAspect4 @Around 开始 →
  8. OrderDemoAspect4 @Before →
  9. OrderDemoAspect4 @AfterRunning →
  10. OrderDemoAspect4 @After →
  11. OrderDemoAspect4 @Around 结束 →
  12. OrderDemoAspect3 @AfterRunning →
  13. OrderDemoAspect3 @After →
  14. OrderDemoAspect3 @Around 结束 →
  15. OrderDemoAspect2 @AfterRunning →
  16. OrderDemoAspect2 @After →
  17. OrderDemoAspect2 @Around 结束 →
  18. OrderDemoAspect1 @AfterRunning →
  19. OrderDemoAspect1 @After →
  20. OrderDemoAspect1 @Around 结束 →

Spring Boot 切面执行优先级配置指南_第1张图片

代码示例

多切面优先级配置

@Aspect
@Component
@Order(1)
public class OrderDemoAspect1 {
    @Before("@annotation(com.shore.annotation.OrderDemoAnnotation)")
    public void before() {
        System.out.println("OrderDemoAspect1 before");
    }

    @After("@annotation(com.shore.annotation.OrderDemoAnnotation)")
    public void after() {
        System.out.println("OrderDemoAspect1 after");
    }

    @AfterReturning(pointcut = "@annotation(com.shore.annotation.OrderDemoAnnotation)", returning = "result")
    public void afterReturning(Object result) {
        System.out.println("OrderDemoAspect1 afterReturning with result: " + result);
    }

    @AfterThrowing(pointcut = "@annotation(com.shore.annotation.OrderDemoAnnotation)", throwing = "exception")
    public void afterThrowing(Throwable exception) {
        System.out.println("OrderDemoAspect1 afterThrowing exception: " + exception);
    }

    @Around("@annotation(com.shore.annotation.OrderDemoAnnotation)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("OrderDemoAspect1 around Before execution of a method (Around advice)");
        // 继续执行原方法
        Object result = joinPoint.proceed(joinPoint.getArgs());

        System.out.println("OrderDemoAspect1 around After execution of a method (Around advice)");
        return result;
    }
}

@Aspect
@Component
@Order(2)
public class OrderDemoAspect2 {
    @Before("@annotation(com.shore.annotation.OrderDemoAnnotation)")
    public void before() {
        System.out.println("OrderDemoAspect2 before");
    }

    @After("@annotation(com.shore.annotation.OrderDemoAnnotation)")
    public void after() {
        System.out.println("OrderDemoAspect2 after");
    }

    @AfterReturning(pointcut = "@annotation(com.shore.annotation.OrderDemoAnnotation)", returning = "result")
    public void afterReturning(Object result) {
        System.out.println("OrderDemoAspect2 afterReturning with result: " + result);
    }

    @AfterThrowing(pointcut = "@annotation(com.shore.annotation.OrderDemoAnnotation)", throwing = "exception")
    public void afterThrowing(Throwable exception) {
        System.out.println("OrderDemoAspect2 afterThrowing exception: " + exception);
    }

    @Around("@annotation(com.shore.annotation.OrderDemoAnnotation)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("OrderDemoAspect2 around Before execution of a method (Around advice)");
        // 继续执行原方法
        Object result = joinPoint.proceed(joinPoint.getArgs());

        System.out.println("OrderDemoAspect2 around After execution of a method (Around advice)");
        return result;
    }
}

@Aspect
@Component
@Order(2)
public class OrderDemoAspect3 {
    @Before("@annotation(com.shore.annotation.OrderDemoAnnotation)")
    public void before() {
        System.out.println("OrderDemoAspect3 before");
    }

    @After("@annotation(com.shore.annotation.OrderDemoAnnotation)")
    public void after() {
        System.out.println("OrderDemoAspect3 after");
    }

    @AfterReturning(pointcut = "@annotation(com.shore.annotation.OrderDemoAnnotation)", returning = "result")
    public void afterReturning(Object result) {
        System.out.println("OrderDemoAspect3 afterReturning with result: " + result);
    }

    @AfterThrowing(pointcut = "@annotation(com.shore.annotation.OrderDemoAnnotation)", throwing = "exception")
    public void afterThrowing(Throwable exception) {
        System.out.println("OrderDemoAspect3 afterThrowing exception: " + exception);
    }

    @Around("@annotation(com.shore.annotation.OrderDemoAnnotation)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("OrderDemoAspect3 around Before execution of a method (Around advice)");
        // 继续执行原方法
        Object result = joinPoint.proceed(joinPoint.getArgs());

        System.out.println("OrderDemoAspect3 around After execution of a method (Around advice)");
        return result;
    }
}

@Aspect
@Component
public class OrderDemoAspect4 {
    @Before("@annotation(com.shore.annotation.OrderDemoAnnotation)")
    public void before() {
        System.out.println("OrderDemoAspect4 before");
    }

    @After("@annotation(com.shore.annotation.OrderDemoAnnotation)")
    public void after() {
        System.out.println("OrderDemoAspect4 after");
    }

    @AfterReturning(pointcut = "@annotation(com.shore.annotation.OrderDemoAnnotation)", returning = "result")
    public void afterReturning(Object result) {
        System.out.println("OrderDemoAspect4 afterReturning with result: " + result);
    }

    @AfterThrowing(pointcut = "@annotation(com.shore.annotation.OrderDemoAnnotation)", throwing = "exception")
    public void afterThrowing(Throwable exception) {
        System.out.println("OrderDemoAspect4 afterThrowing exception: " + exception);
    }

    @Around("@annotation(com.shore.annotation.OrderDemoAnnotation)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("OrderDemoAspect4 around Before execution of a method (Around advice)");
        // 继续执行原方法
        Object result = joinPoint.proceed(joinPoint.getArgs());

        System.out.println("OrderDemoAspect4 around After execution of a method (Around advice)");
        return result;
    }
}

执行结果

OrderDemoAspect1 around Before execution of a method (Around advice)
OrderDemoAspect1 before
OrderDemoAspect2 around Before execution of a method (Around advice)
OrderDemoAspect2 before
OrderDemoAspect3 around Before execution of a method (Around advice)
OrderDemoAspect3 before
OrderDemoAspect4 around Before execution of a method (Around advice)
OrderDemoAspect4 before
OrderDemoAspect4 afterReturning with result: Order Demo
OrderDemoAspect4 after
OrderDemoAspect4 around After execution of a method (Around advice)
OrderDemoAspect3 afterReturning with result: Order Demo
OrderDemoAspect3 after
OrderDemoAspect3 around After execution of a method (Around advice)
OrderDemoAspect2 afterReturning with result: Order Demo
OrderDemoAspect2 after
OrderDemoAspect2 around After execution of a method (Around advice)
OrderDemoAspect1 afterReturning with result: Order Demo
OrderDemoAspect1 after
OrderDemoAspect1 around After execution of a method (Around advice)

注意事项

  1. 优先级生效条件‌:
    • 仅当多个切面作用于‌同一连接点‌时优先级设置才有意义‌。
    • 默认情况下,未指定优先级的切面执行顺序不可预测‌。
  2. 避免重复通知‌:
    • 同类通知(如多个 @Before)默认按方法名字典序执行,@Order 无法直接调整同类通知顺序‌。
  3. 性能优化‌:
    • 优先使用 @Order 注解,避免因实现 Ordered 接口引入额外复杂度‌。

你可能感兴趣的:(spring,boot,后端,java)