
Spring AOP 切面的先后顺序详解当多个切面作用于同一个方法时谁先执行、谁后执行直接影响到事务、日志、权限等逻辑的正确性。理解切面顺序的控制方式和执行规律是写好 AOP 的关键。一、默认顺序不可预测如果不指定顺序多个切面的执行顺序由 Spring 容器加载 Bean 的顺序决定而 Bean 的加载顺序又受包扫描顺序、类名、依赖关系等因素影响。结果是不确定的不同环境、不同启动顺序下可能不一样。AspectComponentpublicclassLogAspect{Before(serviceLayer())publicvoidbefore(){System.out.println(LogAspect);}}AspectComponentpublicclassTransactionAspect{Before(serviceLayer())publicvoidbefore(){System.out.println(TransactionAspect);}}两个切面谁先执行无法保证。生产环境中依赖默认顺序是危险的必须显式指定。二、控制顺序的两种方式1. Order 注解数值越小优先级越高越先执行。AspectComponentOrder(1)publicclassLogAspect{// 优先级高先执行}AspectComponentOrder(2)publicclassTransactionAspect{// 优先级低后执行}2. 实现 Ordered 接口AspectComponentpublicclassLogAspectimplementsOrdered{OverridepublicintgetOrder(){return1;}}两种方式等价。Order更简洁Ordered接口更灵活可以在运行时动态计算顺序。三、执行顺序的规律多个切面作用于同一个方法时执行顺序遵循栈结构优先级高的切面在外层优先级低的在内层。假设有两个切面LogAspect的Order(1)TransactionAspect的Order(2)。1. Before 的执行顺序数值小的先执行。LogAspect Before ← Order(1) 先执行 TransactionAspect Before ← Order(2) 后执行 目标方法2. After 的执行顺序数值大的先执行。目标方法 TransactionAspect After ← Order(2) 先执行 LogAspect After ← Order(1) 后执行3. 整体结构LogAspect Before TransactionAspect Before 目标方法 TransactionAspect After LogAspect AfterBefore按Order升序执行After按Order降序执行。就像穿衣服和脱衣服先穿内衣再穿外套脱的时候先脱外套再脱内衣。4. 完整示例AspectComponentOrder(1)publicclassLogAspect{Before(serviceLayer())publicvoidbefore(){System.out.println(1. LogAspect Before);}After(serviceLayer())publicvoidafter(){System.out.println(6. LogAspect After);}Around(serviceLayer())publicObjectaround(ProceedingJoinPointpjp)throwsThrowable{System.out.println(2. LogAspect Around 前);Objectresultpjp.proceed();System.out.println(5. LogAspect Around 后);returnresult;}}AspectComponentOrder(2)publicclassTransactionAspect{Before(serviceLayer())publicvoidbefore(){System.out.println(3. TransactionAspect Before);}After(serviceLayer())publicvoidafter(){System.out.println(4. TransactionAspect After);}}输出1. LogAspect Before 2. LogAspect Around 前 3. TransactionAspect Before 目标方法执行 4. TransactionAspect After 5. LogAspect Around 后 6. LogAspect After执行顺序分析Around在Before之后但在其他切面的Before之前。目标方法执行完后先执行内层切面的After再执行外层切面的Around后置逻辑和After。5. Around 的嵌套关系Around的proceed()调用链体现了切面的嵌套结构LogAspect Around 前 TransactionAspect Around 前 目标方法 TransactionAspect Around 后 LogAspect Around 后Order小的切面在外层proceed()时进入内层执行完内层后返回外层。四、多个切面的完整执行流程有三个切面Order分别为 1、2、3AspectComponentOrder(1)publicclassAspectA{Around(serviceLayer())publicObjectaround(ProceedingJoinPointpjp)throwsThrowable{System.out.println(A 前);Objectresultpjp.proceed();System.out.println(A 后);returnresult;}}AspectComponentOrder(2)publicclassAspectB{Around(serviceLayer())publicObjectaround(ProceedingJoinPointpjp)throwsThrowable{System.out.println(B 前);Objectresultpjp.proceed();System.out.println(B 后);returnresult;}}AspectComponentOrder(3)publicclassAspectC{Around(serviceLayer())publicObjectaround(ProceedingJoinPointpjp)throwsThrowable{System.out.println(C 前);Objectresultpjp.proceed();System.out.println(C 后);returnresult;}}输出A 前 B 前 C 前 目标方法执行 C 后 B 后 A 后结构就像一个洋葱A 在最外层C 在最内层。proceed()逐层深入返回时逐层退出。五、特殊场景的顺序1. 事务切面与日志切面通常事务切面的优先级要高于日志切面让事务在外层AspectComponentOrder(1)// 事务在外层publicclassTransactionAspect{}AspectComponentOrder(2)// 日志在内层publicclassLogAspect{}这样日志记录的是事务提交后的行为如果事务回滚日志也能正确记录异常。2. 权限切面与业务切面权限校验应该在最外层先校验再执行AspectComponentOrder(1)// 权限校验优先publicclassPermissionAspect{}AspectComponentOrder(2)publicclassTransactionAspect{}权限不通过时直接抛异常不需要开启事务。3. 缓存切面与事务切面缓存切面通常在外层先查缓存未命中再进入事务AspectComponentOrder(1)// 缓存在外层publicclassCacheAspect{}AspectComponentOrder(2)// 事务在内层publicclassTransactionAspect{}六、Spring 内置切面的顺序Spring 内置了一些切面它们的Order值切面作用默认 OrderExposeInvocationInterceptor暴露AopContextOrdered.HIGHEST_PRECEDENCE 1AspectJPointcutAdvisorAspectJ 切面由Order决定TransactionInterceptor事务Ordered.LOWEST_PRECEDENCE默认AsyncAnnotationBeanPostProcessor异步默认较低TransactionInterceptor默认的 Order 是Ordered.LOWEST_PRECEDENCE也就是最内层。如果自定义切面不加Order它的默认值也是LOWEST_PRECEDENCE可能和事务切面顺序不确定。七、常见问题1. 不加 Order 会怎样多个切面都不加Order时顺序取决于 Bean 加载顺序不可控。生产环境必须显式指定Order。2. Order 值相同会怎样Order值相同时顺序仍然不确定。应保证每个切面的Order值唯一。3. Order 范围Order接受int类型。常用范围是0到100或者使用Ordered.HIGHEST_PRECEDENCEInteger.MIN_VALUE和Ordered.LOWEST_PRECEDENCEInteger.MAX_VALUE。4. 方法级 OrderOrder也可以标注在方法上控制同一个切面内多个通知的顺序。但同一个切面内的通知顺序比较固定Around外层Before内层一般不需要方法级Order。5. 切面内部顺序同一个切面内Around在Before之前After在Around之后。这个顺序由 Spring 内部决定不受Order影响。八、总结维度规律控制方式Order注解或Ordered接口数值规律数值越小优先级越高Before顺序优先级高的先执行升序After顺序优先级高的后执行降序Around结构优先级高的在外层proceed()进入内层默认顺序不可预测必须显式指定典型排列权限(1) → 日志(2) → 事务(3) → 缓存(4) 或按业务需求调整切面顺序的核心规律是优先级高的在外层优先级低的在内层。Before从外到内After从内到外Around通过proceed()逐层深入再逐层返回。理解这个栈式结构就能正确安排事务、日志、权限、缓存的相对位置。