DEV Community

Liu yu
Liu yu

Posted on

Spring AOP拦截器链

你的理解部分正确,但需要更精准的修正。Spring AOP 的拦截器链顺序和实际执行顺序之间存在递归推进关系,这需要结合拦截器的具体实现来理解。我们以你的代码为例详细分析:


1. 拦截器链的构造顺序

在你的代码中,@Before@After 两个通知会被转换为以下拦截器,并按如下顺序加入链中:

  1. MethodBeforeAdviceInterceptor(对应 @Before
  2. AspectJAfterAdvice(对应 @After

拦截器链的顺序是:@Before@After(注意:此时原始方法尚未执行!)。


2. 拦截器链的执行顺序(关键)

ReflectiveMethodInvocation.proceed() 被调用时,拦截器链的执行流程如下:

(1) 初始状态

  • currentInterceptorIndex = -1
  • 拦截器链顺序:[MethodBeforeAdviceInterceptor, AspectJAfterAdvice]

(2) 第一次调用 proceed()

  • currentInterceptorIndex 递增为 0(第一个拦截器:MethodBeforeAdviceInterceptor
  • 执行 MethodBeforeAdviceInterceptor.invoke(this)
  // 伪代码:MethodBeforeAdviceInterceptor 的逻辑
  public Object invoke(MethodInvocation mi) {
      // 1. 先执行前置通知(@Before)
      this.advice.before(...);
      // 2. 继续调用 mi.proceed() → 触发下一个拦截器
      return mi.proceed();
  }
Enter fullscreen mode Exit fullscreen mode
  • 此时会打印 "AOP 前置通知",然后继续调用 mi.proceed()

(3) 第二次调用 proceed()

  • currentInterceptorIndex 递增为 1(第二个拦截器:AspectJAfterAdvice
  • 执行 AspectJAfterAdvice.invoke(this)
  // 伪代码:AspectJAfterAdvice 的逻辑
  public Object invoke(MethodInvocation mi) {
      try {
          // 1. 继续调用 mi.proceed() → 此时已无后续拦截器,触发原始方法调用
          Object result = mi.proceed();
          return result;
      } finally {
          // 2. 无论是否异常,执行后置通知(@After)
          invokeAdviceMethod(...);
      }
  }
Enter fullscreen mode Exit fullscreen mode
  • 调用 mi.proceed() 时,currentInterceptorIndex 已达到链的末尾(1 == 1),因此执行原始方法 invokeJoinpoint() → 打印 "执行业务逻辑"
  • 最后,在 finally 块中打印 "AOP 后置通知"

3. 执行顺序总结

虽然拦截器链的顺序是 @Before@After,但实际执行顺序是:

@Before → 原始方法 → @After
Enter fullscreen mode Exit fullscreen mode

这是因为 @After 对应的拦截器(AspectJAfterAdvice)在它的 invoke() 方法中,先调用 mi.proceed() 触发后续逻辑(包括原始方法),然后在 finally 块中执行后置通知。


关键设计思想

  • 递归式链式调用:每个拦截器在完成自身逻辑后,必须显式调用 mi.proceed() 才能推进链的执行。
  • 后置通知的 finally 保证@After 的逻辑包裹在 finally 块中,确保无论方法是否抛出异常都会执行。
  • 拦截器顺序 ≠ 执行顺序:拦截器的排列顺序决定了它们拦截的优先级,但具体执行顺序由每个拦截器的内部逻辑决定。

验证方法

  1. 调试 ReflectiveMethodInvocation

    • proceed() 方法中观察 currentInterceptorIndex 的变化。
    • MethodBeforeAdviceInterceptor.invoke()AspectJAfterAdvice.invoke() 中设置断点,观察调用顺序。
  2. 查看拦截器链内容

    • CglibAopProxy.DynamicAdvisedInterceptor.intercept() 方法中,检查拦截器链的组成:
     List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
    
  3. 观察 AspectJAfterAdviceinvoke() 方法

    • 注意 mi.proceed() 的调用触发原始方法,而 @After 逻辑在 finally 块中执行。

总结流程图

1. 代理方法调用
   │
   └─ ReflectiveMethodInvocation.proceed()
        │
        ├─ 执行 MethodBeforeAdviceInterceptor(@Before)
        │    │
        │    └─ 调用 proceed() → 推进到下一个拦截器
        │
        ├─ 执行 AspectJAfterAdvice(@After)
        │    │
        │    ├─ 调用 proceed() → 触发原始方法
        │    │    │
        │    │    └─ 执行原始方法(TargetService.doSomething())
        │    │
        │    └─ finally 块执行 @After 逻辑
        │
        └─ 返回结果
Enter fullscreen mode Exit fullscreen mode

通过这种设计,Spring AOP 实现了 @Before → 原始方法 → @After 的执行顺序,而拦截器链的物理顺序只是实现这一逻辑的手段。

Top comments (0)