⚙️ 一、通知到 MethodInterceptor
的转换机制
Spring AOP 通过适配器模式 将开发者定义的注解型通知(如 @Before
)统一转换为 MethodInterceptor
接口实现,确保所有通知类型能接入同一调用链。以下是转换细节:
1. 适配器实现原理
- 核心接口:
MethodInterceptor
是所有通知的最终形态,其invoke(MethodInvocation mi)
方法封装了通知逻辑与链式调用逻辑。 - 适配器作用:将不同通知类型(如
AspectJMethodBeforeAdvice
)包装为MethodInterceptor
子类,实现逻辑转换。
2. 各通知类型的转换细节
下表总结了五种通知的转换逻辑与执行顺序:
通知类型 | 原始接口 | 转换后实现(实际执行拦截的类) | invoke()核心逻辑 |
---|---|---|---|
@Before | AspectJMethodBeforeAdvice | MethodBeforeAdviceInterceptor | 1. 调用advice.before() 2. 调用mi.proceed() |
@After | AspectJAfterAdvice | AspectJAfterAdvice | 1. try { mi.proceed() } 2. finally { 执行advice方法 } |
@AfterReturning | AspectJAfterReturningAdvice | AfterReturningAdviceInterceptor | 1. 调用mi.proceed() 2. 如果正常返回,调用advice.afterReturning(returnValue) |
@AfterThrowing | AspectJAfterThrowingAdvice | AspectJAfterThrowingAdvice | 1. try { mi.proceed() } 2. catch (异常ex) { 调用advice.afterThrowing(ex) } (注意:只捕获声明的异常类型) |
@Around | 无 | AspectJAroundAdvice | 1. 通过反射调用环绕通知方法,并传入ProceedingJoinPoint 2. 在环绕通知内部通过ProceedingJoinPoint.proceed()控制调用链 |
关键设计 :除
@Around
外,所有通知均被转换为环绕通知形式 ,通过统一的invoke()
接口接入调用链。
🔄 二、MethodInvocation
与拦截器链的协同流程
1. 调用链构建过程
- 代理对象创建 :Spring 容器启动时,扫描所有
Advisor
(包含通知与切点)。 - 适配器转换 :通过
AdvisorAdapterRegistry
将Advisor
中的通知转换为MethodInterceptor
。 - 链式存储 :生成
List<MethodInterceptor>
并注入ReflectiveMethodInvocation
。
2. 调用链执行逻辑(ReflectiveMethodInvocation.proceed()
)
java
public class ReflectiveMethodInvocation implements MethodInvocation {
private final List<MethodInterceptor> interceptors;
private int currentInterceptorIndex = -1; // 当前执行位置索引
public Object proceed() throws Throwable {
// 1. 所有拦截器执行完毕 → 反射调用目标方法
if (this.currentInterceptorIndex == interceptors.size() - 1) {
return invokeJoinpoint();
}
// 2. 获取下一个拦截器并推进索引
MethodInterceptor interceptor = interceptors.get(++currentInterceptorIndex);
// 3. 执行当前拦截器(触发统一的invoke接口)
return interceptor.invoke(this); // 将自身(MethodInvocation)传入
}
}
索引机制 :
currentInterceptorIndex
记录当前执行位置,每次proceed()
调用时递增,实现拦截器的顺序触发。
3. 各通知在调用链中的协作顺序
以下序列图展示了典型调用流程(含多个通知类型):
sequenceDiagram
participant MI as ReflectiveMethodInvocation
participant Before as Before拦截器
participant Around as Around拦截器
participant After as After拦截器
participant Target as 目标方法
MI->>Before: interceptor.invoke(this)
activate Before
Before-->>Before: 执行@Before逻辑
Before->>MI: 调用proceed()
deactivate Before
MI->>Around: interceptor.invoke(this)
activate Around
Around-->>Around: 环绕前逻辑
Around->>MI: 调用proceed()
MI->>After: interceptor.invoke(this)
activate After
After->>MI: 调用proceed()
MI->>Target: 反射调用目标方法
Target-->>After: 返回结果
After-->>After: 执行@After逻辑
After-->>Around: 返回
deactivate After
Around-->>Around: 环绕后逻辑
Around-->>Before: 返回
deactivate Around
Before-->>MI: 返回最终结果
协作关键:
- 嵌套执行:每个拦截器通过调用
mi.proceed()
触发后续拦截器或目标方法,形成嵌套调用栈。- 逻辑控制权:拦截器可决定是否调用
proceed()
。例如权限校验失败时,Before
拦截器可不调用proceed()
,直接中断链。
🧩 三、统一 MethodInterceptor
的设计价值
1. 外部易用性
开发者通过直观注解(如 @Before
)声明切面,无需理解底层调用链。适配器模式隐藏了复杂性,例如:
java
@Before("execution(* com.dwl.*.*(..))")
public void logBefore(JoinPoint jp) {
System.out.println("Before: " + jp.getSignature());
}
2. 内部统一性
- 单一执行逻辑:
ReflectiveMethodInvocation.proceed()
只需遍历List<MethodInterceptor>
,无需区分通知类型。 - 扩展性:新增通知类型时,只需实现适配器并注册到
AdvisorAdapterRegistry
,无需修改调用链核心逻辑。
3. 异常处理优势
- 统一异常传播:异常沿调用链反向传递,由最近的
@AfterThrowing
或@Around
拦截器捕获。 - 资源清理保证:
@After
逻辑在finally
块执行,确保即使目标方法异常也能运行(如关闭数据库连接)。
⚡ 四、完整流程案例:日志切面执行
假设切面包含 @Before
、@Around
、@After
通知,调用链构建与执行如下:
-
转换阶段:
@Before
→MethodBeforeAdviceInterceptor
@Around
→AspectJAroundAdvice
@After
→AfterAdviceInterceptor
-
调用链执行顺序:
flowchart LR A[Before拦截器] -->|调用proceed| B[Around拦截器] B -->|调用proceed| C[After拦截器] C -->|调用proceed| D[目标方法] D -->|返回| C C -->|执行finally逻辑| B B -->|执行环绕后逻辑| A -
异常场景:若目标方法抛出异常:
After
拦截器在finally
中执行日志清理。- 异常传递给Around拦截器,由其捕获并记录。
💎 总结
- 转换必然性:适配器模式是 Spring AOP 的基石,将注解通知统一转为
MethodInterceptor
,实现调用链标准化。 - 协同核心:
ReflectiveMethodInvocation
通过索引控制与嵌套调用(proceed()
)协调拦截器执行,形成责任链模式。 - 设计价值:
- 对外:简化开发,通过注解屏蔽底层复杂度。
- 对内:通过统一接口减少冗余代码,提升扩展性。
- 健壮性:异常处理与资源清理机制保障业务逻辑安全。
此设计完美体现了 "开闭原则":新增通知类型无需修改调用链核心,仅需扩展适配器。