【Spring】AOP核心之原始对象与代理对象

一文彻底搞懂 Spring 原始对象与代理对象(AOP核心原理)

在学习 Spring 的过程中,经常会听到几个概念:

  • 原始对象(Target Object)
  • 代理对象(Proxy Object)
  • 动态代理
  • AOP
  • CGLIB / JDK Proxy

很多人知道:

复制代码
Spring 的 AOP 是通过代理实现的

但很多人并不清楚:

  • 什么是原始对象?
  • 什么是代理对象?
  • Spring 为什么要创建代理对象?
  • 代理对象是如何生成的?
  • 事务、日志、权限为什么必须使用代理?

本篇文章将 彻底讲清 Spring 中原始对象与代理对象的核心原理


一、什么是原始对象(Target Object)

1 概念

原始对象(Target Object) 指的是:

开发者编写的真实业务对象。

例如:

java 复制代码
@Service
public class UserService {

    public void createUser() {
        System.out.println("创建用户");
    }

}

这个 UserService 对象就是 原始对象

它只负责:

复制代码
业务逻辑

例如:

复制代码
下单
创建用户
查询数据

2 原始对象的问题

假设我们有如下需求:

复制代码
调用 createUser 方法时
需要:
1 记录日志
2 开启事务
3 权限检查
4 统计耗时

如果直接写在业务代码里:

java 复制代码
public void createUser(){

    //日志
    log.info("方法开始");

    //权限
    checkPermission();

    //业务
    System.out.println("创建用户");

}

问题:

复制代码
业务代码被大量非业务逻辑污染

这违反了:

复制代码
单一职责原则

因此 Spring 引入了:

复制代码
AOP

而 AOP 的核心实现就是:

复制代码
代理对象

二、什么是代理对象(Proxy Object)

1 概念

代理对象(Proxy Object) 指的是:

Spring 在运行时生成的一个对象,用来"代理"原始对象。

调用关系变成:

复制代码
调用者
   ↓
代理对象
   ↓
原始对象

流程:

复制代码
Controller
   ↓
Proxy(UserService)
   ↓
UserService

代理对象会在调用原始方法前后 添加额外逻辑

例如:

复制代码
日志
事务
权限
监控

2 代理对象执行流程

假设调用:

java 复制代码
userService.createUser();

真实执行流程:

复制代码
调用代理对象
      ↓
代理对象执行前置逻辑
      ↓
调用原始对象方法
      ↓
执行后置逻辑

伪代码:

java 复制代码
public void createUser(){

    //前置增强
    log.info("方法开始");

    //调用原始方法
    target.createUser();

    //后置增强
    log.info("方法结束");

}

三、为什么 Spring 要使用代理?

Spring 需要解决的问题是:

复制代码
在不修改业务代码的情况下
增加功能

例如:

复制代码
事务
日志
权限
缓存
监控

如果不用代理:

复制代码
每个方法都写事务代码

代码会变成:

java 复制代码
try{

    transaction.begin();

    userService.createUser();

    transaction.commit();

}catch(Exception e){
    transaction.rollback();
}

非常复杂。

而使用代理:

复制代码
开发者只需要写
@Transactional

Spring 自动完成事务逻辑。


四、Spring 中哪些功能依赖代理

Spring 中很多功能都是通过 代理对象实现的

功能 原理
AOP 动态代理
@Transactional 事务代理
@Cacheable 缓存代理
@Async 异步代理
@Retryable 重试代理

例如:

java 复制代码
@Transactional
public void createUser(){
}

Spring 会生成:

复制代码
UserServiceProxy

五、Spring 代理的两种实现方式

Spring 代理有两种实现方式。


1 JDK 动态代理

JDK 自带代理机制。

要求:

复制代码
必须有接口

示例:

java 复制代码
public interface UserService {
    void createUser();
}

实现类:

java 复制代码
@Service
public class UserServiceImpl implements UserService {

    public void createUser(){
        System.out.println("创建用户");
    }

}

Spring 生成:

复制代码
UserServiceProxy

代理接口。

特点:

复制代码
代理接口

2 CGLIB 代理

如果 没有接口,Spring 使用:

复制代码
CGLIB

CGLIB 通过:

复制代码
继承类

实现代理。

例如:

java 复制代码
@Service
public class UserService {

    public void createUser(){
        System.out.println("创建用户");
    }

}

Spring 会生成:

复制代码
UserService$$EnhancerBySpring

本质:

复制代码
继承 UserService

并重写方法。


六、Spring 如何选择代理方式

Spring 默认规则:

复制代码
有接口 → JDK代理
无接口 → CGLIB代理

Spring Boot 2 之后:

复制代码
默认 CGLIB

可以配置:

java 复制代码
@EnableAspectJAutoProxy(proxyTargetClass = true)

七、Spring AOP 执行流程

完整调用链:

复制代码
Controller
   ↓
Proxy
   ↓
Advice(前置通知)
   ↓
目标方法
   ↓
Advice(后置通知)

AOP 术语:

名称 说明
Target 原始对象
Proxy 代理对象
Advice 增强逻辑
Aspect 切面
JoinPoint 连接点

八、如何判断自己拿到的是代理对象

可以打印类名:

java 复制代码
System.out.println(userService.getClass());

输出:

复制代码
class com.demo.UserService$$EnhancerBySpringCGLIB

说明:

复制代码
这是代理对象

九、Spring 代理常见坑(非常重要)


1 同类方法调用失效(事务失效)

例如:

java 复制代码
@Service
public class UserService {

    public void methodA(){
        methodB();
    }

    @Transactional
    public void methodB(){
    }

}

问题:

复制代码
methodB 的事务不会生效

原因:

复制代码
没有经过代理对象

调用流程:

复制代码
this.methodB()

而不是:

复制代码
proxy.methodB()

2 private 方法不能代理

例如:

java 复制代码
@Transactional
private void test(){
}

事务不会生效。

原因:

复制代码
CGLIB 无法重写 private 方法

3 final 方法无法代理

因为:

复制代码
CGLIB 通过继承实现代理

而:

复制代码
final 方法不能被重写

十、原始对象 vs 代理对象总结

对比 原始对象 代理对象
创建者 开发者 Spring
作用 业务逻辑 增强逻辑
是否增强
是否拦截方法
AOP使用 不直接使用 实际使用

调用关系:

复制代码
Controller
   ↓
代理对象
   ↓
原始对象

十一、面试高频问题


1 什么是代理对象?

答:

复制代码
代理对象是 Spring 在运行时生成的对象,
用于在调用目标方法前后添加增强逻辑。

2 Spring AOP 为什么使用代理?

答:

复制代码
为了在不修改业务代码的情况下
增加功能(事务、日志、权限等)。

3 Spring 代理方式有哪些?

复制代码
JDK动态代理
CGLIB代理

4 JDK 和 CGLIB 区别?

JDK CGLIB
代理方式 接口 继承
是否需要接口
性能 稍慢 稍快

5 为什么事务会失效?

原因通常是:

复制代码
没有经过代理对象调用

例如:

复制代码
同类方法调用
private方法
final方法

十二、一句话理解代理对象

Spring 的核心思想:

复制代码
你写业务对象
Spring 给你生成代理对象

真正调用的是:

复制代码
代理对象

而不是:

复制代码
原始对象
相关推荐
君爱学习2 小时前
SpringCloud-微服务拆分
java
礼拜天没时间.2 小时前
力扣热题100实战 | 第25期:K个一组翻转链表——从两两交换到K路翻转的进阶之路
java·算法·leetcode·链表·递归·链表反转·k个一组翻转链表
w_a_o2 小时前
传统配方+机器学习:福尔蒂新材料用15年经验构建梯度回归预测模型(Python开源预告)
python·机器学习·回归·kmeans·宽度优先
jiet_h2 小时前
Python tempfile 深入实战:安全、优雅地处理临时文件与临时目录
python
y = xⁿ2 小时前
【从零开始学习Redis|第四篇】从底层理解缓存问题:雪崩、击穿、穿透与一致性设计
java·redis·学习·缓存
江湖有缘2 小时前
本地化JSON 处理新方案:基于 Docker的JSON Hero部署全记录
java·docker·json
摩尔曼斯克的海2 小时前
力扣面试题--双指针类
python·算法·leetcode
witAI2 小时前
gemini3.1拆短剧2025解析,多模态模型如何重塑内容创作流程
人工智能·python
love530love2 小时前
Windows 11 源码编译 vLLM 0.16 完全指南(CUDA 12.6 / PyTorch 2.7.1+cu126)
人工智能·pytorch·windows·python·深度学习·comfyui·vllm