Spring AOP 中有多个切面时执行顺序是怎样的?
当多个切面(Aspect)的切点(Pointcut)都匹配到同一个连接点(方法)时,Spring AOP 需要一个明确的规则来决定这些切面(或通知)的执行顺序。
核心规则:@Order
注解或 Ordered
接口
Spring AOP 使用 @Order
注解 或实现 org.springframework.core.Ordered
接口 来定义切面的执行顺序。
@Order(value)
: 注解的值是一个整数。value
的值越小,切面的优先级越高,越先执行。Ordered
接口: 实现这个接口需要重写getOrder()
方法,返回一个整数。同样,返回值越小,优先级越高。
记忆技巧:可以想象成排队,号码越小(@Order(1)
)的人排在越前面,越先办理业务。
“洋葱模型”:切面的进入与退出顺序
多个切面的执行顺序遵循一个经典的“同心圆”或“洋葱模型”。优先级高的切面会“包裹”在优先级低的切面的外面。
这意味着:
-
进入阶段 (Before/Around前置部分):
@Order
值越小的切面,其@Before
或@Around
的前置部分越先执行。
-
退出阶段 (After/Around后置部分):
@Order
值越小的切面,其@After
、@AfterReturning
、@AfterThrowing
或@Around
的后置部分越后执行(因为它在最外层,需要等待所有内层逻辑执行完毕)。
图解“洋葱模型”:
假设我们有两个切面:LoggingAspect (@Order(10))
和 SecurityAspect (@Order(20))
。
优先级:LoggingAspect
> SecurityAspect
。
<-- 请求进入 响应退出 -->
+-----------------------------------------------------------------------------+
| LoggingAspect (@Order(10) - 优先级高,在最外层) |
| |
| @Before/Around前置 (先执行) |
| |
| +------------------------------------------------------------------+ |
| | SecurityAspect (@Order(20) - 优先级低,在内层) | |
| | | |
| | @Before/Around前置 (后执行) | |
| | | |
| | +------------------------------------------------------+ | |
| | | 目标方法 (Target Method) | | |
| | +------------------------------------------------------+ | |
| | | |
| | @After/Around后置 (先执行) | |
| | | |
| +------------------------------------------------------------------+ |
| |
| @After/Around后置 (后执行) |
| |
+-----------------------------------------------------------------------------+
执行顺序总结:
LoggingAspect
的@Before
SecurityAspect
的@Before
目标方法
SecurityAspect
的@After
/@AfterReturning
LoggingAspect
的@After
/@AfterReturning
代码示例
让我们创建两个切面并指定它们的顺序。
1. 安全切面 (优先级最高)
package com.example.aop;import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;@Aspect
@Component
@Order(1) // 优先级最高,数字最小
public class SecurityAspect {@Before("execution(* com.example.service.*.*(..))")public void checkSecurity() {System.out.println("--- [SecurityAspect @Order(1)]: Checking security... ---");}
}
2. 日志切面 (优先级较低)
package com.example.aop;import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;@Aspect
@Component
@Order(10) // 优先级较低,数字较大
public class LoggingAspect {@Before("execution(* com.example.service.*.*(..))")public void logBefore() {System.out.println("--- [LoggingAspect @Order(10)]: Logging before method execution... ---");}@After("execution(* com.example.service.*.*(..))")public void logAfter() {System.out.println("--- [LoggingAspect @Order(10)]: Logging after method execution... ---");}
}
3. 目标服务
package com.example.service;import org.springframework.stereotype.Service;@Service
public class MyService {public void doWork() {System.out.println("****** Executing core business logic in MyService.doWork() ******");}
}
4. 运行
当你调用 myService.doWork()
时,控制台的输出会是:
--- [SecurityAspect @Order(1)]: Checking security... ---
--- [LoggingAspect @Order(10)]: Logging before method execution... ---
****** Executing core business logic in MyService.doWork() ******
--- [LoggingAspect @Order(10)]: Logging after method execution... ---
结果分析:
- Before 通知:
@Order(1)
的SecurityAspect
先于@Order(10)
的LoggingAspect
执行。 - After 通知:如果我们在
SecurityAspect
中也添加一个@After
通知,那么它将会比LoggingAspect
的@After
更晚执行,因为它在“洋葱”的最外层。
特殊情况:同一个切面内的通知顺序
如果多个不同类型的通知(@Before
, @After
等)定义在同一个 @Aspect
类中,并且都匹配了同一个方法,它们的执行顺序是固定的,不受 @Order
注解影响。
其执行顺序遵循标准的 AOP 流程:
@Around
(前置部分)@Before
- 目标方法
@Around
(后置部分)@After
(最终通知)@AfterReturning
(如果成功) 或@AfterThrowing
(如果异常)
注意:同一个切面内,多个相同类型的通知(例如两个 @Before
通知)的执行顺序是不确定的,你不应该依赖于它们的顺序。如果需要严格的顺序,应该将它们合并到一个通知方法中,或者拆分到不同的切面并通过 @Order
来控制。
总结
- 使用
@Order(整数)
或实现Ordered
接口来控制多个切面之间的执行顺序。 @Order
的值越小,优先级越高。- 高优先级的切面像洋葱一样包裹低优先级的切面。
- 这意味着高优先级切面的前置逻辑先执行,后置逻辑后执行。
- 同一个切面内的通知顺序是固定的,无法通过
@Order
改变。