目录
在Spring AOP中,多个切面(Aspect)的执行顺序是一个需要特别注意的问题,尤其在涉及环绕通知(@Around)、前置/后置通知(@Before/@After)以及异常通知时。理解其规则对保证切面逻辑的正确性至关重要。
📌 核心规则总结
-
优先级(
@Order或Ordered接口):-
数值越小,优先级越高。
-
@Order(1)的切面优先级高于@Order(2)的切面。 -
未指定
@Order的切面,其优先级是Ordered.LOWEST_PRECEDENCE(即Integer.MAX_VALUE,优先级最低)。 -
实现
Ordered接口并重写getOrder()方法可以达到与@Order注解相同的效果。
-
-
执行阶段:
-
"进入"目标方法阶段: 切面按 优先级从高到低 的顺序执行它们的
@Before通知 和@Around通知中调用proceed()之前的部分。 -
"退出"目标方法阶段: 切面按 优先级从低到高 的顺序执行它们的
@AfterReturning/@AfterThrowing/@After通知 和@Around通知中调用proceed()之后的部分。
-
-
通知类型内部顺序:
-
同一个切面内的不同通知类型,执行顺序遵循AOP标准:
-
@Around(前置处理)→ @Before → 目标方法执行 → @Around(后置处理)→ @AfterReturning(正常返回)| @AfterThrowing(异常处理)→ @After(最终处理)
-
-
@After通知总是在@AfterReturning或@AfterThrowing之前执行。 这有时会让人意外。
-
🚀 详细执行流程(多个切面)
假设你有两个切面:LoggingAspect (@Order(1)) 和 SecurityAspect (@Order(2)),它们都作用于同一个连接点(如某个Service方法)。
🔄 正常执行流程(无异常)
-
进入阶段(高优先级 -> 低优先级):
-
LoggingAspect @Around(开始部分 - 记录开始时间等) -
LoggingAspect @Before -
SecurityAspect @Around(开始部分 - 权限检查等) -
SecurityAspect @Before
-
-
目标方法执行:
-
实际的业务方法被调用执行。
-
-
退出阶段(低优先级 -> 高优先级):
-
SecurityAspect @After(清理资源等 - 注意这个先执行!) -
SecurityAspect @AfterReturning(处理正常返回结果) -
SecurityAspect @Around(结束部分 - 记录执行时间、处理返回值等) -
LoggingAspect @After(清理资源等) -
LoggingAspect @AfterReturning(记录返回值等) -
LoggingAspect @Around(结束部分 - 记录总耗时等)
-
🚨 异常执行流程(目标方法抛出异常)
-
进入阶段(高优先级 -> 低优先级): (与正常流程相同)
-
LoggingAspect @Around(开始部分) -
LoggingAspect @Before -
SecurityAspect @Around(开始部分) -
SecurityAspect @Before
-
-
目标方法执行: 抛出异常。
-
退出阶段(低优先级 -> 高优先级):
-
SecurityAspect @After(清理资源等 - 无论异常都执行) -
SecurityAspect @AfterThrowing(处理捕获的异常) -
SecurityAspect @Around(结束部分 - 如果捕获了异常,可以在这里处理或重新抛出) -
LoggingAspect @After(清理资源等) -
LoggingAspect @AfterThrowing(记录异常信息等) -
LoggingAspect @Around(结束部分 - 如果捕获了异常,可以在这里处理或重新抛出) -
异常最终会传播给调用者(除非在某个通知中被捕获并处理掉)。
-
📊 关键点图示
[调用流程]
1. 进入阶段 (高优先级 -> 低优先级):
[调用者]
↓
[Logging @Around (start)] → [Logging @Before] → [Security @Around (start)] → [Security @Before]
2. 目标方法执行:
[Security @Before] → [Target Method] ← [Security @Around (start)]
3. 退出阶段 (正常流程):
[Target Method] → [Security @After] → [Security @AfterReturning] → [Security @Around (end)]
↓
[Logging @After] ← [Logging @AfterReturning] ← [Logging @Around (end)]
↓
[调用者 (正常返回)]
4. 退出阶段 (异常流程):
[Target Method] → [Security @After] → [Security @AfterThrowing] → [Security @Around (end) - catch/handle]
↓
[Logging @After] ← [Logging @AfterThrowing] ← [Logging @Around (end) - catch/handle]
↓
[调用者 (收到异常)]
� 常见陷阱与注意事项
-
@After的执行时机:@After通知总是在@AfterReturning或@AfterThrowing之前执行。这是由AOP代理的实现机制决定的。如果需要在@AfterReturning之后做一些事情,通常应该把逻辑放在@AfterReturning通知内部或@Around的结束部分。 -
@Around中的proceed():@Around通知必须调用proceed()来执行目标方法(或下一个切面/通知)。忘记调用proceed()会导致目标方法永远不会执行。proceed()的返回值就是目标方法的返回值(或下一个切面修改后的返回值)。 -
异常处理: 如果在
@Around通知中捕获了proceed()抛出的异常,并在通知内部处理掉(不重新抛出),那么@AfterThrowing通知将不会被触发,因为异常已经被"吃掉"了。调用者也不会收到异常。 -
未指定顺序: 如果多个切面都没有指定
@Order,它们的执行顺序是未定义的(取决于类加载顺序、Bean注册顺序等,不可靠)。强烈建议为所有切面显式指定@Order。 -
理解优先级数值:
@Order(1)优先级高于@Order(2)。数值越小,优先级越高,越早执行"进入"部分,越晚执行"退出"部分。 -
切面本身也是Bean: 切面类本身是Spring管理的Bean。确保它们能被Spring正确扫描和实例化。复杂的Bean依赖或代理机制也可能间接影响顺序(显式使用
@Order是最可靠的方式)。
🛠 如何控制顺序
-
使用
@Order注解: 这是最推荐、最常用的方式。直接在切面类上标注@Order(value)。@Aspect @Component @Order(1) // 高优先级切面 public class LoggingAspect { ... } @Aspect @Component @Order(2) // 低优先级切面 public class SecurityAspect { ... } -
实现
Ordered接口: 让切面类实现org.springframework.core.Ordered接口,并重写getOrder()方法。@Aspect @Component public class TransactionAspect implements Ordered { @Override public int getOrder() { return 0; // 设置为最高优先级 } // ... 其他通知方法 ... } -
在Spring Boot中使用
@AutoConfigureAfter(较少用): 主要用于自动配置类之间的顺序,不常用于普通业务切面。
💡 总结
掌握Spring AOP切面执行顺序的关键在于:
-
@Order决定切面优先级: 数值越小,优先级越高。 -
"进入"顺序: 高优先级 -> 低优先级 (
@Before,@Around前半部分)。 -
"退出"顺序: 低优先级 -> 高优先级 (
@After,@AfterReturning/@AfterThrowing,@Around后半部分)。 -
@After总是先于@AfterReturning/@AfterThrowing执行。 -
务必显式指定
@Order以避免不可预测的行为。
通过合理设置 @Order 值并深刻理解上述流程,你可以精确控制多个切面的协作逻辑,确保事务、日志、安全、缓存等横切关注点按预期顺序生效。 💪
5120

被折叠的 条评论
为什么被折叠?



