Spring AOP MethodInvocation 工作原理

⚙️ 一、通知到 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. 调用链构建过程

  1. 代理对象创建 :Spring 容器启动时,扫描所有Advisor(包含通知与切点)。
  2. 适配器转换 :通过AdvisorAdapterRegistryAdvisor中的通知转换为MethodInterceptor
  3. 链式存储 :生成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 通知,调用链构建与执行如下:

  1. 转换阶段

    • @BeforeMethodBeforeAdviceInterceptor
    • @AroundAspectJAroundAdvice
    • @AfterAfterAdviceInterceptor
  2. 调用链执行顺序

    flowchart LR A[Before拦截器] -->|调用proceed| B[Around拦截器] B -->|调用proceed| C[After拦截器] C -->|调用proceed| D[目标方法] D -->|返回| C C -->|执行finally逻辑| B B -->|执行环绕后逻辑| A
  3. 异常场景:若目标方法抛出异常:

    • After 拦截器在 finally 中执行日志清理。
    • 异常传递给Around拦截器,由其捕获并记录。

💎 总结

  1. 转换必然性:适配器模式是 Spring AOP 的基石,将注解通知统一转为MethodInterceptor,实现调用链标准化。
  2. 协同核心:ReflectiveMethodInvocation通过索引控制与嵌套调用(proceed())协调拦截器执行,形成责任链模式。
  3. 设计价值:
    • 对外:简化开发,通过注解屏蔽底层复杂度。
    • 对内:通过统一接口减少冗余代码,提升扩展性。
    • 健壮性:异常处理与资源清理机制保障业务逻辑安全。

此设计完美体现了 "开闭原则":新增通知类型无需修改调用链核心,仅需扩展适配器。

相关推荐
是梦终空4 分钟前
JAVA毕业设计253—基于Java+Springboot+vue3+协同过滤推荐算法的传统服饰文化平台(源代码+数据库+任务书+12000字论文)
java·spring boot·vue·毕业设计·课程设计·协同过滤推荐算法·传统服饰文化平台
七夜zippoe6 分钟前
告别API碎片化与高成本 - 用AI Ping打造下一代智能编程工作流
人工智能·架构·大模型·智能编程·ai ping·模型聚合
Lei活在当下7 小时前
【Perfetto从入门到精通】2. 使用 Perfetto 追踪/分析 APP 的 Native/Java 内存
android·性能优化·架构
HerayChen7 小时前
HbuilderX 内存溢出报错
java·大数据·linux
程序员小白条8 小时前
0经验如何找实习?
java·开发语言·数据结构·数据库·链表
Xの哲學8 小时前
Linux Miscdevice深度剖析:从原理到实战的完整指南
linux·服务器·算法·架构·边缘计算
小马爱打代码8 小时前
Spring AI:搭建自定义 MCP Server:获取 QQ 信息
java·人工智能·spring
郭涤生8 小时前
QT 架构笔记
java·数据库·系统架构
韩立学长8 小时前
基于Springboot流浪动物领养网站0kh2iyb4(程序、源码、数据库、调试部署方案及开发环境)系统界面展示及获取方式置于文档末尾,可供参考。
数据库·spring boot·后端
daidaidaiyu8 小时前
基于LangGraph开发复杂智能体学习一则
java·ai