当前位置: 首页 > news >正文

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

当多个切面(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 改变。

相关文章:

  • 云南网站建设一条龙网站页面优化内容包括哪些
  • 做网站5年工资多少seo站内优化站外优化
  • 微信小程序电脑端打开黑帽seo联系方式
  • 做网站好还是网页好万秀服务不错的seo推广
  • 教育网页设计网站陕西百度推广的代理商
  • 西安大型网站设计公司郑州网络推广效果
  • 深度学习的人工神经元
  • Node.js 版本管理工具对比:nvm、n、volta
  • 盘口语言 开盘三分钟知涨跌
  • Git常用操作详解
  • Java-中断流程控制
  • .net动态代理模式
  • window显示驱动开发—支持 DXGI DDI(二)
  • 智哪儿专访 | Matter中国提速:开放标准如何破局智能家居“生态孤岛”?
  • 图像融合中损失函数【4】--复杂图像特征的损失函数
  • 南北差异之——理解业务和理解产品
  • 机器学习-线性模型
  • 强化学习概述
  • 第八课:大白话教你逻辑回归
  • QT 学习笔记摘要(三)
  • 使用GithubActions和腾讯CloudBase自动发布静态网页
  • Excel基础:选择和移动
  • 从零构建 gRPC 跨语言通信:C++ 服务端与 C# 客户端完整指南
  • Python Web开发领域异步 I/O库之sanic使用详解
  • 教育培训教学通用PPT模版
  • LVS-DR负载均衡群集深度实践:高性能架构设计与排障指南