
简单来说:
- 目标对象 (Target Object):就是你亲手编写的、包含核心业务逻辑的那个原始对象(比如
UserService,ProductServiceImpl)。它是一个纯粹的业务类,完全不知道 AOP 的存在。 - 代理对象 (Proxy Object):是 Spring AOP 框架在运行时动态创建的一个“包装”对象。它在外部看起来和目标对象一模一样(实现了相同的接口或继承了相同的类),但内部包含了额外的“切面逻辑”(即你的通知代码)。
我们最终调用的是哪一个?
答案是:我们调用的代理对象 (Proxy Object)。
详细区别与图解
让我们用一个生动的比喻来解释:
- 目标对象 (Target Object):一位重要的 VIP(比如一位科学家)。他的职责是进行核心的科学研究(业务逻辑)。
- 代理对象 (Proxy Object):这位 VIP 的 贴身保镖/经纪人。这位保镖看起来和 VIP 穿得差不多,甚至能模仿他的签名。他的职责是在 VIP 工作前后处理各种事务(比如安检、记录来访者、安排媒体采访等 —— 这些就是切面逻辑)。
- 你 (调用方):一个想要拜访这位 VIP 的访客。
当你去拜访时,你不会直接冲到 VIP 面前。你首先接触到的是保镖(代理对象)。
- 你向保镖提出请求:“我想见 VIP,让他帮我解决一个科学问题。” (你调用了代理对象的方法)
- 保镖(代理对象)收到请求后,并不会立刻让你去见 VIP。他会先执行一系列操作:
@Before:检查你的身份和预约(前置通知)。@Around(前置部分):记录下你的来访时间(环绕通知的前半部分)。
- 一切准备就绪后,保镖才会去通知 VIP(代理对象调用目标对象):“这位访客可以见了,请您开始工作吧。”
- VIP(目标对象)开始埋头进行科学研究(执行核心业务逻辑)。
- 研究完成后,VIP 把结果交给了保镖。
- 保镖拿到结果后,他还会做一些收尾工作:
@Around(后置部分):记录下会谈结束的时间,并计算总耗时(环绕通知的后半部分)。@AfterReturning:将研究成果整理成一份漂亮的报告(返回通知)。@After:打扫会议室,做好清理工作(后置通知)。
- 最后,保镖(代理对象)把这份漂亮的报告交给你。
在这个过程中,你自始至终只和保镖(代理对象)打交道,甚至可能都没意识到 VIP 本人只负责了中间最核心的那一小段工作。
调用流程图
+---------------------------------+
| 你 (调用方,例如 Controller) |
+---------------------------------+
| 1. 调用 userService.findUser()
v
+-------------------------------------------------------------------------+
| 代理对象 (Proxy Object) |
| (由 Spring AOP 在运行时动态创建, e.g., UserServiceProxy) |
| |
| +---------------------------+ |
| | 2. @Before 通知执行 | |
| +---------------------------+ |
| | |
| +---------------------------+ 3. 代理内部调用目标对象的原始方法 +-----------------+
| | @Around (前半部分) 执行 |------------------------------------->| 目标对象 (Target)|
| +---------------------------+ | (你写的UserService)|
| | | | 4. 核心业务逻辑 |
| +---------------------------+ 5. 目标方法返回结果给代理 | | 执行 |
| | @Around (后半部分) 执行 |<--------------------------------------| +-------+---------+
| +---------------------------+ | ^
| | | |
| +---------------------------+ | |
| | @AfterReturning 通知执行 | | |
| +---------------------------+ | |
| | | |
| +---------------------------+ | |
| | @After 通知执行 | | |
| +---------------------------+ |
| |
+-------------------------------------------------------------------------+
| 6. 最终结果返回给调用方
v
+---------------------------------+
| 你 (调用方,例如 Controller) |
+---------------------------------+
区别总结
| 特性 | 目标对象 (Target Object) | 代理对象 (Proxy Object) |
|---|---|---|
| 创建者 | 开发者 (你写的 .java 文件) | Spring AOP 框架 (在运行时动态生成) |
| 包含内容 | 纯粹的、核心的业务逻辑 | 切面逻辑 (所有通知) + 一个对目标对象的引用 |
| 对AOP的感知 | 完全无感知,它是一个 POJO (Plain Old Java Object) | 为 AOP 而生,它的存在就是为了执行切面逻辑 |
| 谁调用它 | 代理对象在内部调用它 | 外部调用方 (如 Controller, 其他 Service) |
| 在Spring容器中 | 原始的 Bean 定义是你写的 Target 类 | 当需要 AOP 增强时,Spring 用代理对象替换了容器中的目标对象 |
这为什么重要?—— “自我调用”失效问题
理解这个区别能帮我们避开一个常见的 AOP 陷阱:自我调用 (self-invocation) 失效。
看下面的例子:
@Service
public class UserServiceImpl implements UserService {
public void methodA() {
System.out.println("--- Executing method A ---");
// 这里是关键!`this` 指向的是目标对象本身,而不是代理对象!
this.methodB(); // 这个调用会绕过 AOP 代理
}
// 我们希望这个方法被 AOP 拦截
@MyAopAnnotation
public void methodB() {
System.out.println("--- Executing method B ---");
}
}
当你从外部调用 userService.methodA() 时:
- 你调用的是代理对象的
methodA。 methodA的 AOP 通知(如果有的话)会正常执行。- 进入
methodA的目标对象内部。 - 当它执行
this.methodB()时,this指的是目标对象本身,而不是代理对象。这次调用就像是在对象内部调用一个普通的私有方法一样,它直接访问了methodB的代码,完全绕过了代理对象。 - 因此,
methodB上的@MyAopAnnotation所对应的 AOP 通知不会被触发!
结论:AOP 的增强功能只在通过代理对象进行外部调用时才会生效。对象内部的自我调用(this.xxx())会使 AOP 失效。
308

被折叠的 条评论
为什么被折叠?



