【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 给你生成代理对象

真正调用的是:

复制代码
代理对象

而不是:

复制代码
原始对象
相关推荐
liu****4 分钟前
LangChain-AI应用开发框架(六)
人工智能·python·langchain·大模型应用·本地部署大模型
心有—林夕14 分钟前
两个事务间的传播机制
java·事务
疯狂成瘾者18 分钟前
什么是多 Agent,多Agent是如何协作的?
java
witAI25 分钟前
**AI仿真人剧制作2025推荐,专业团队与创新技术引领未来**
人工智能·python
he___H25 分钟前
Spring中的设计模式
java·spring·设计模式
liuyao_xianhui31 分钟前
优选算法_最小基因变化_bfs_C++
java·开发语言·数据结构·c++·算法·哈希算法·宽度优先
做一个AK梦33 分钟前
计算机系统概论知识点(软件设计师)
java·开发语言
♪-Interpretation41 分钟前
第五节:Python的流程控制语句
python
東雪木1 小时前
Java学习——一访问修饰符(public/protected/default/private)的权限控制本质
java·开发语言·学习·java面试
两点王爷1 小时前
docker 创建和使用存储卷相关内容
java·docker·容器