Spring AOP `MethodInvocation` 工作原理
⚙️ 一、通知到 MethodInterceptor
的转换机制
Spring AOP 通过适配器模式将开发者定义的注解型通知(如 @Before
)统一转换为 MethodInterceptor
接口实现,确保所有通知类型能接入同一调用链。以下是转换细节:
1. 适配器实现原理
- 核心接口:
MethodInterceptor
是所有通知的最终形态,其invoke(MethodInvocation mi)
方法封装了通知逻辑与链式调用逻辑。 - 适配器作用:将不同通知类型(如
AspectJMethodBeforeAdvice
)包装为MethodInterceptor
子类,实现逻辑转换。
2. 各通知类型的转换细节
下表总结了五种通知的转换逻辑与执行顺序:
invoke()
核心逻辑AspectJMethodBeforeAdvice
MethodBeforeAdviceInterceptor
advice.before()
2. 调用
mi.proceed()
触发后续链AspectJAfterAdvice
AfterAdviceInterceptor
try { mi.proceed() }
2.
finally { advice.after() }
(确保最终执行)AspectJAfterReturningAdvice
AfterReturningAdviceInterceptor
mi.proceed()
2. 若正常返回,执行
advice.afterReturning()
AspectJAfterThrowingAdvice
ThrowsAdviceInterceptor
try { mi.proceed() }
2.
catch(ex) { advice.afterThrowing(ex) }
AspectJAroundAdvice
2. 通过
mi.proceed()
触发后续链(需主动调用)关键设计:除
@Around
外,所有通知均被转换为环绕通知形式,通过统一的invoke()
接口接入调用链。
🔄 二、MethodInvocation
与拦截器链的协同流程
1. 调用链构建过程
- 代理对象创建:Spring 容器启动时,扫描所有
Advisor
(包含通知与切点)。 - 适配器转换:通过
AdvisorAdapterRegistry
将Advisor
中的通知转换为MethodInterceptor
。 - 链式存储:生成
List
并注入ReflectiveMethodInvocation
。
2. 调用链执行逻辑(ReflectiveMethodInvocation.proceed()
)
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. 各通知在调用链中的协作顺序
以下序列图展示了典型调用流程(含多个通知类型):
#mermaid-svg-IMt91Xd96Ov8JZ8M {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-IMt91Xd96Ov8JZ8M .error-icon{fill:#552222;}#mermaid-svg-IMt91Xd96Ov8JZ8M .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-IMt91Xd96Ov8JZ8M .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-IMt91Xd96Ov8JZ8M .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-IMt91Xd96Ov8JZ8M .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-IMt91Xd96Ov8JZ8M .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-IMt91Xd96Ov8JZ8M .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-IMt91Xd96Ov8JZ8M .marker{fill:#333333;stroke:#333333;}#mermaid-svg-IMt91Xd96Ov8JZ8M .marker.cross{stroke:#333333;}#mermaid-svg-IMt91Xd96Ov8JZ8M svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-IMt91Xd96Ov8JZ8M .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-IMt91Xd96Ov8JZ8M text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-IMt91Xd96Ov8JZ8M .actor-line{stroke:grey;}#mermaid-svg-IMt91Xd96Ov8JZ8M .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-IMt91Xd96Ov8JZ8M .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-IMt91Xd96Ov8JZ8M #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-IMt91Xd96Ov8JZ8M .sequenceNumber{fill:white;}#mermaid-svg-IMt91Xd96Ov8JZ8M #sequencenumber{fill:#333;}#mermaid-svg-IMt91Xd96Ov8JZ8M #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-IMt91Xd96Ov8JZ8M .messageText{fill:#333;stroke:#333;}#mermaid-svg-IMt91Xd96Ov8JZ8M .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-IMt91Xd96Ov8JZ8M .labelText,#mermaid-svg-IMt91Xd96Ov8JZ8M .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-IMt91Xd96Ov8JZ8M .loopText,#mermaid-svg-IMt91Xd96Ov8JZ8M .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-IMt91Xd96Ov8JZ8M .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-IMt91Xd96Ov8JZ8M .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-IMt91Xd96Ov8JZ8M .noteText,#mermaid-svg-IMt91Xd96Ov8JZ8M .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-IMt91Xd96Ov8JZ8M .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-IMt91Xd96Ov8JZ8M .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-IMt91Xd96Ov8JZ8M .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-IMt91Xd96Ov8JZ8M .actorPopupMenu{position:absolute;}#mermaid-svg-IMt91Xd96Ov8JZ8M .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-IMt91Xd96Ov8JZ8M .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-IMt91Xd96Ov8JZ8M .actor-man circle,#mermaid-svg-IMt91Xd96Ov8JZ8M line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-IMt91Xd96Ov8JZ8M :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;}ReflectiveMethodInvocationBefore拦截器Around拦截器After拦截器目标方法interceptor.invoke(this)1执行@Before逻辑2调用proceed()3interceptor.invoke(this)4环绕前逻辑5调用proceed()6interceptor.invoke(this)7调用proceed()8反射调用目标方法9返回结果10执行@After逻辑11返回12环绕后逻辑13返回14返回最终结果15ReflectiveMethodInvocationBefore拦截器Around拦截器After拦截器目标方法
协作关键:
- 嵌套执行:每个拦截器通过调用
mi.proceed()
触发后续拦截器或目标方法,形成嵌套调用栈。- 逻辑控制权:拦截器可决定是否调用
proceed()
。例如权限校验失败时,Before
拦截器可不调用proceed()
,直接中断链。
🧩 三、统一 MethodInterceptor
的设计价值
1. 外部易用性
开发者通过直观注解(如 @Before
)声明切面,无需理解底层调用链。适配器模式隐藏了复杂性,例如:
@Before(\"execution(* com.dwl.*.*(..))\")public void logBefore(JoinPoint jp) { System.out.println(\"Before: \" + jp.getSignature());}
2. 内部统一性
- 单一执行逻辑:
ReflectiveMethodInvocation.proceed()
只需遍历List
,无需区分通知类型。 - 扩展性:新增通知类型时,只需实现适配器并注册到
AdvisorAdapterRegistry
,无需修改调用链核心逻辑。
3. 异常处理优势
- 统一异常传播:异常沿调用链反向传递,由最近的
@AfterThrowing
或@Around
拦截器捕获。 - 资源清理保证:
@After
逻辑在finally
块执行,确保即使目标方法异常也能运行(如关闭数据库连接)。
⚡ 四、完整流程案例:日志切面执行
假设切面包含 @Before
、@Around
、@After
通知,调用链构建与执行如下:
-
转换阶段:
@Before
→MethodBeforeAdviceInterceptor
@Around
→AspectJAroundAdvice
@After
→AfterAdviceInterceptor
-
调用链执行顺序:
#mermaid-svg-n44jBskR63mwMcxG {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-n44jBskR63mwMcxG .error-icon{fill:#552222;}#mermaid-svg-n44jBskR63mwMcxG .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-n44jBskR63mwMcxG .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-n44jBskR63mwMcxG .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-n44jBskR63mwMcxG .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-n44jBskR63mwMcxG .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-n44jBskR63mwMcxG .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-n44jBskR63mwMcxG .marker{fill:#333333;stroke:#333333;}#mermaid-svg-n44jBskR63mwMcxG .marker.cross{stroke:#333333;}#mermaid-svg-n44jBskR63mwMcxG svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-n44jBskR63mwMcxG .label{font-family:\"trebuchet ms\",verdana,arial,sans-serif;color:#333;}#mermaid-svg-n44jBskR63mwMcxG .cluster-label text{fill:#333;}#mermaid-svg-n44jBskR63mwMcxG .cluster-label span{color:#333;}#mermaid-svg-n44jBskR63mwMcxG .label text,#mermaid-svg-n44jBskR63mwMcxG span{fill:#333;color:#333;}#mermaid-svg-n44jBskR63mwMcxG .node rect,#mermaid-svg-n44jBskR63mwMcxG .node circle,#mermaid-svg-n44jBskR63mwMcxG .node ellipse,#mermaid-svg-n44jBskR63mwMcxG .node polygon,#mermaid-svg-n44jBskR63mwMcxG .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-n44jBskR63mwMcxG .node .label{text-align:center;}#mermaid-svg-n44jBskR63mwMcxG .node.clickable{cursor:pointer;}#mermaid-svg-n44jBskR63mwMcxG .arrowheadPath{fill:#333333;}#mermaid-svg-n44jBskR63mwMcxG .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-n44jBskR63mwMcxG .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-n44jBskR63mwMcxG .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-n44jBskR63mwMcxG .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-n44jBskR63mwMcxG .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-n44jBskR63mwMcxG .cluster text{fill:#333;}#mermaid-svg-n44jBskR63mwMcxG .cluster span{color:#333;}#mermaid-svg-n44jBskR63mwMcxG div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-n44jBskR63mwMcxG :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;}调用proceed调用proceed调用proceed返回执行finally逻辑执行环绕后逻辑Before拦截器Around拦截器After拦截器目标方法
-
异常场景:若目标方法抛出异常:
After
拦截器在finally
中执行日志清理。- 异常传递给Around拦截器,由其捕获并记录。
💎 总结
- 转换必然性:适配器模式是 Spring AOP 的基石,将注解通知统一转为
MethodInterceptor
,实现调用链标准化。 - 协同核心:
ReflectiveMethodInvocation
通过索引控制与嵌套调用(proceed()
)协调拦截器执行,形成责任链模式。 - 设计价值:
- 对外:简化开发,通过注解屏蔽底层复杂度。
- 对内:通过统一接口减少冗余代码,提升扩展性。
- 健壮性:异常处理与资源清理机制保障业务逻辑安全。
此设计完美体现了 “开闭原则”:新增通知类型无需修改调用链核心,仅需扩展适配器。