bigywater.tech
Spring · 21

通知执行顺序:Around、Before、After、AfterReturning 与 @Order

多个切面、多种通知同时作用在一个方法上,谁先谁后?@After 和 @AfterReturning 谁在前?@Order 写在哪里才有用?@Around 能做什么?这篇在 Spring Framework 7.0.9 上用带编号的时间线逐项验证。

11 步
两个切面、每个五种通知,正常返回一次调用的步骤数
第 1 对第 3
实现 Ordered 的切面与 @Bean 方法上标 @Order(-5) 的切面的执行位置:@Bean 方法上的 @Order 没有生效
0 次
Swallow 切面在 @Around 里不调用 proceed() 时目标方法被执行的次数

一次调用的完整顺序

实测 · 本机执行输出
  --- 正常返回 ok()(A 的 @Order(1),B 的 @Order(2))
  1. A.around 前
  2. A.before
  3.   B.around 前
  4.   B.before
  5.     ▶ 目标方法 ok()
  6.   B.afterReturning
  7.   B.after
  8.   B.around 后(finally)
  9. A.afterReturning
  10. A.after(finally 型)
  11. A.around 后(finally)
  --- 抛异常 fail()
  1. A.around 前
  2. A.before
  3.   B.around 前
  4.   B.before
  5.     ▶ 目标方法 fail() 抛异常
  6.   B.afterThrowing
  7.   B.after
  8.   B.around 后(finally)
  9. A.afterThrowing
  10. A.after(finally 型)
  11. A.around 后(finally)
  12. 调用者收到 IllegalStateException

顺序小的切面在外层;同一个切面内按 Around、Before、目标、AfterReturning 或 AfterThrowing、After 展开。

定义顺序无关

实测 · 本机执行输出
  --- 正常返回 ok()(A 的 @Order(1),B 的 @Order(2);这次 @Bean 的定义顺序是 B 先 A 后)
  1. A.around 前
  2. A.before
  3.   B.around 前
  4.   B.before
  5.     ▶ 目标方法 ok()
  6.   B.afterReturning
  7.   B.after
  8.   B.around 后(finally)
  9. A.afterReturning
  10. A.after(finally 型)
  11. A.around 后(finally)
  --- 抛异常 fail()
  1. A.around 前
  2. A.before
  3.   B.around 前

有 @Order 时 @Bean 的定义顺序不影响通知顺序。

同切面内的同类通知

实测 · 本机执行输出
  --- 同一个切面里的多个通知,没有 @Order
  1. Names.mmm(@Around)前
  2. Names.aaa(后声明)
  3. Names.zzz(先声明)
  4.     ▶ 目标方法 ok()
  5. Names.yyy(@After)
  6. Names.mmm(@Around)后

看起来按方法名排序,不是按声明顺序。

@Around 的权力

实测 · 本机执行输出
  --- @Around 里修改参数后 proceed(Object[])
  1. Skip.around 看到参数 [原值],改成 [改过]后 proceed
  2.     ▶ 目标方法 arg(改过)
  3. 返回值 = 改过
  --- @Around 不调用 proceed()
  1. Swallow.around 没有调用 proceed(),直接返回 "缓存值"
  2. 返回值 = 缓存值

可以改参数、不调用 proceed() 直接返回。

顺序的来源

实测 · 本机执行输出
  --- 三个切面:Plain(没有顺序)、OrderedViaBean(@Bean 方法上 @Order(-5))、OrderedViaInterface(实现 Ordered,getOrder=0)
  1. OrderedViaInterface(getOrder=0).before
  2. PlainNoOrder.before
  3. OrderedViaBean.before
  4.     ▶ 目标方法 ok()

类上的 @Order 与 Ordered 接口有效,@Bean 方法上的 @Order 对切面没有效果。

分步演示

通知的执行顺序
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK 21.0.12(Homebrew) / Spring Boot 4.0.8(Spring Framework 7.0.9、Tomcat 11.0.24) / Spring Cloud 2025.1.3;用 javac 编译、java 直接运行,依赖由 Maven 从 Maven Central 解析;数字每次运行会有小幅抖动。用不带 Boot 的 Spring Framework 7.0.9 + AspectJ 注解,proxyTargetClass = true;“同类型的通知按方法名排序”是根据一个例子(aaa 与 zzz)的现象下的结论,只测了方法名与声明顺序相反的一种情形;“@Bean 方法上的 @Order 无效”只测了 @Aspect 切面这一种用法
    • 没有验证:Spring 5.2.7 之前 @After 与 @AfterReturning 的顺序(这里只看 7.0.9);@Order 写在 @Aspect 类上与 @Component 扫描方式的组合;多个切面的 @Around 嵌套下异常传播;Advisor(非注解风格)与切面的顺序;事务拦截器与自定义切面的顺序(见事务篇)
    京ICP备2026067502号-1京公网安备11010502063487号