Spring AOP 中有多个切面时执行顺序是怎样的?

发布于:2025-06-26 ⋅ 阅读:(19) ⋅ 点赞:(0)

当多个切面(Aspect)的切点(Pointcut)都匹配到同一个连接点(方法)时,Spring AOP 需要一个明确的规则来决定这些切面(或通知)的执行顺序。

核心规则:@Order 注解或 Ordered 接口

Spring AOP 使用 @Order 注解 或实现 org.springframework.core.Ordered 接口 来定义切面的执行顺序。

  • @Order(value): 注解的值是一个整数。value 的值越小,切面的优先级越高,越先执行。
  • Ordered 接口: 实现这个接口需要重写 getOrder() 方法,返回一个整数。同样,返回值越小,优先级越高。

记忆技巧:可以想象成排队,号码越小(@Order(1))的人排在越前面,越先办理业务。

“洋葱模型”:切面的进入与退出顺序

多个切面的执行顺序遵循一个经典的“同心圆”或“洋葱模型”。优先级高的切面会“包裹”在优先级低的切面的外面。

这意味着:

  1. 进入阶段 (Before/Around前置部分)

    • @Order越小的切面,其 @Before@Around 的前置部分越先执行
  2. 退出阶段 (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后置 (后执行)                                                |
|                                                                             |
+-----------------------------------------------------------------------------+

执行顺序总结:

  1. LoggingAspect@Before
  2. SecurityAspect@Before
  3. 目标方法
  4. SecurityAspect@After / @AfterReturning
  5. 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 流程:

  1. @Around (前置部分)
  2. @Before
  3. 目标方法
  4. @Around (后置部分)
  5. @After (最终通知)
  6. @AfterReturning (如果成功) 或 @AfterThrowing (如果异常)

注意:同一个切面内,多个相同类型的通知(例如两个 @Before 通知)的执行顺序是不确定的,你不应该依赖于它们的顺序。如果需要严格的顺序,应该将它们合并到一个通知方法中,或者拆分到不同的切面并通过 @Order 来控制。

总结

  • 使用 @Order(整数) 或实现 Ordered 接口来控制多个切面之间的执行顺序。
  • @Order值越小,优先级越高
  • 高优先级的切面像洋葱一样包裹低优先级的切面。
  • 这意味着高优先级切面的前置逻辑先执行后置逻辑后执行
  • 同一个切面内的通知顺序是固定的,无法通过 @Order 改变。

网站公告

今日签到

点亮在社区的每一天
去签到