当前位置: 首页 > news >正文

一个代理对象被调用时,方法调用的链路是怎样的?

当一个被 AOP 增强的代理对象的方法被调用时,其链路遵循一个经典的责任链模式 (Chain of Responsibility Pattern),通常被描述为“洋葱模型”或“拦截器链 (Interceptor Chain)”。

核心脉络:一个递归的调用链

整个过程不是简单的 代理 -> 目标,而是一个 代理 -> 通知1 -> 通知2 -> ... -> 目标 的链式调用。

关键角色:

  1. 代理对象 (Proxy Object): 外部调用的入口。
  2. 拦截器/通知 (Interceptors/Advices): 每一个 @Before, @Around 等通知都会被包装成一个统一的 MethodInterceptor 对象。
  3. ReflectiveMethodInvocation: 这是整个调用链的“发动机”和“上下文管理器”。它封装了目标对象、目标方法、参数以及一个即将执行的拦截器列表。它的核心方法是 proceed()

详细的方法调用链路步骤

假设我们有一个 UserService 的代理对象,它被两个切面(一个日志切面,一个安全切面)增强了。

调用链路如下:

1. 调用命中代理对象

外部代码(如 Controller)调用 userService.doSomething()。这个调用首先命中代理对象

2. 代理激活,准备执行链
  • JDK 代理: JdkDynamicAopProxyinvoke() 方法被触发。
  • CGLIB 代理: CglibAopProxyintercept() 方法被触发。

这两个方法的首要任务是:
a. 根据当前被调用的方法 (doSomething),从所有已配置的切面中,找出所有适用的通知(Advisors)。
b. 将这些通知包装成一个拦截器链 (List of MethodInterceptor)

3. 创建并启动调用“发动机” (ReflectiveMethodInvocation)

Spring 创建一个 ReflectiveMethodInvocation 对象。这个对象非常重要,它包含了本次调用的所有信息:

  • 目标对象 (UserService 实例)
  • 目标方法 (doSomething 方法)
  • 方法参数 (args 数组)
  • 上一步创建的拦截器链
  • 一个内部计数器,用于追踪当前执行到链中的哪个位置 (e.g., currentInterceptorIndex)

然后,它会第一次调用 invocation.proceed() 方法,启动整个链条。

4. proceed() 的递归调用 (核心)

proceed() 方法的内部逻辑是整个链路的核心,它是一个巧妙的递归:

a. 检查链条是否走完:
* proceed() 首先会检查内部计数器,看拦截器链是否已经执行完毕。

b. 如果链条未走完:
* 它从拦截器链中取出当前的拦截器(比如,第一个是安全切面的拦截器)。
* 然后调用这个拦截器的 invoke(this) 方法,并把 ReflectiveMethodInvocation 自身作为参数传进去。
* 关键点:每一个拦截器(尤其是 @Around 通知)的实现,在执行完自己的“前置逻辑”后,又会去调用它接收到的那个 invocation 对象的 proceed() 方法。

c. 递归向下:
* 这个 invocation.proceed() 调用会再次触发上面的逻辑:检查计数器、取出下一个拦截器(日志切面的拦截器)、调用它的 invoke()
* 这个过程不断重复,形成了一个调用栈,控制权从外层通知传递到内层通知。

d. 如果链条已走完:
* 当计数器发现后面已经没有拦截器了,最后的 proceed() 调用会执行它的终极任务:通过 Java 反射,真正地调用原始目标对象的方法 (target.doSomething())。

5. 返回值的“出栈”之旅

a. 目标方法执行完毕后,会返回一个结果(或抛出异常)。
b. 这个结果会返回给最后一个调用 proceed() 的拦截器(日志切面)。
c. 日志切面拿到结果,可以执行它的“后置逻辑”(例如,打印返回值),然后将结果返回。
d. 这个结果又会返回给倒数第二个拦截器(安全切面)。
e. 安全切面执行它的后置逻辑,再将结果返回…
f. 这个过程不断重复,直到结果最终从最外层的拦截器返回,并由代理对象交还给最初的调用方。

图解调用链路 (Onion Model)

Caller|v
Proxy.doSomething()|+-> Jdk/Cglib Interceptor.invoke()|+-> Create ReflectiveMethodInvocation (contains [SecurityInterceptor, LogInterceptor])|+-> invocation.proceed()|+-> SecurityInterceptor.invoke(invocation)||  System.out.println("Security check before...");|+-> invocation.proceed()  // <--- 递归调用|+-> LogInterceptor.invoke(invocation)||  System.out.println("Log before...");|+-> invocation.proceed() // <--- 递归调用|+-> No more interceptors, invoke target method!|v+---------------------+| Target.doSomething()|  <-- 核心业务+---------------------+|<-- return "Success"||  String result = "Success";|  System.out.println("Log after...");|  return result;|<--- return "Success"||  String result = "Success";|  System.out.println("Security check after...");|  return result;|<-- return "Success"||<-- return "Success"|<-- return "Success"|v
Caller

@Around 通知的关系

你可能会觉得这个 invocation.proceed() 很眼熟。没错,当你在编写 @Around 通知时,你拿到的 ProceedingJoinPoint 对象,它其实就是 ReflectiveMethodInvocation 的一个门面 (Facade)

你调用的 pjp.proceed(),在底层就是触发了我们上面描述的那个递归调用链的下一步。

总结

  1. 责任链模式: 整个调用链路是一个标准的责任链,每个通知都是链上的一个节点。
  2. 递归驱动: ReflectiveMethodInvocation.proceed() 方法通过递归调用,将控制权依次传递给链上的每一个通知,最后到达目标方法。
  3. 上下文传递: ReflectiveMethodInvocation 对象作为上下文,贯穿整个调用链,保存着调用状态。
  4. 洋葱模型: 调用过程像剥洋葱一样,从外层进入,到达核心(目标方法),再从内层一层层返回。
http://www.lqws.cn/news/584785.html

相关文章:

  • 【Kafka使用方式以及原理】
  • 安卓获取图片(相机拍摄/相册选择)
  • Android Telephony 网络状态中的 NAS 信息
  • window显示驱动开发—在注册表中设置 DXGI 信息
  • 【C语言】知识总结·内存函数
  • 三态门Multisim电路仿真——硬件工程师笔记
  • 优雅草蜻蜓HR人才招聘系统v2.0.9上线概要 -优雅草新产品上线
  • Amazon Athena:无服务器交互式查询服务的终极解决方案
  • 33. 搜索旋转排序数组
  • pytorch底层原理学习--PyTorch 架构梳理
  • FreePDFv3.0.0:颠覆你的文献阅读习惯
  • 16014.rtsp推流服务器
  • C++ 第四阶段 STL 容器 - 第五讲:详解 std::set 与 std::unordered_set
  • TDH社区开发版安装教程
  • [学习]M-QAM的数学原理与调制解调原理详解(仿真示例)
  • [面试]手写题-Promise.all() Promise.race()
  • 机器学习20-线性网络思考
  • 第三十六章 CAN——控制器局域网络接口
  • 字节跳动 C++ QT PC客户端面试
  • 论文中用matplotlib画的图,如何保持大小一致。
  • Vue2中使用DHTMLX Gantt
  • 深入理解Webpack的灵魂:Tapable插件架构解析
  • 使用Dirichlet分布进行随机初始化
  • 文心大模型 4.5 系列开源首发:技术深度解析与应用指南
  • StackGAN(堆叠生成对抗网络)
  • vscode 改注释的颜色,默认是灰色的,想改成红色
  • Prompt Enginering
  • 会议室预约系统的典型架构
  • Prompt 精通之路(一)- AI 时代的新语言:到底什么是 Prompt?为什么它如此重要?
  • Python 数据分析与机器学习入门 (五):Matplotlib 数据可视化基础