spring Context中代理对象和原始对象存储关系

⚛️前言

Spring容器管理Bean的方式以及代理机制可能会让问题变得复杂,但是需要明确一点:

  1. ​Spring容器中默认存储的是代理对象​

    • 当某个Bean被AOP增强时(如通过@Transactional, @Cacheable等),Spring容器实际创建并管理的是​代理对象​,而不是原始对象。
  2. ​原始对象在哪?​

    • 代理对象内部持有一个对原始对象(也叫目标对象)的引用。这个原始对象由代理对象内部维护,但并不直接暴露在Spring应用上下文中。 在 Spring 框架中,这个问题涉及到 Bean 的生命周期管理和 AOP 代理机制的核心设计。以下是详细解释:

🛄图示内存结构:

java 复制代码
Spring 应用上下文 (ApplicationContext)
│
├─── Bean名称: "payService"
│      类型: PayService$$EnhancerBySpringCGLIB$$...   [代理对象]
│      内部结构:
│          ┌─────────────────────┐
│          │   CGLIB代理实例      │
│          │  ┌────────────────┐ │
│          │  │  TargetSource ────> 原始PayService对象 [在代理内部]
│          │  └────────────────┘ │
│          └─────────────────────┘
│
└─── 其他Bean...
    

🌱 Spring 容器中对象的存储机制

对象类型 存储位置 生命周期管理 可见性
​代理对象​ Spring 应用上下文(ApplicationContext) 由 Spring 容器完全管理 唯一对外暴露的 Bean
​原始对象​ 代理对象内部 (作为 TargetSource) 由代理对象管理 对开发者不可见

🔍 核心设计图解

graph TD subgraph Spring ApplicationContext A[Bean: payServiceProxy] --> B[TargetSource] B --> C[原始对象实例] end
  1. ​容器启动阶段​​:

    • Spring 创建原始对象(目标对象)
    • 根据 AOP 配置创建代理对象(JDK 动态代理或 CGLIB 代理)
    • ​只将代理对象注册为 Spring Bean​
    • 原始对象作为代理的内部状态被封装
  2. ​依赖注入时​​:

    java 复制代码
    @Autowired
    private PayService payService; // 这里获取的实际是代理对象
  3. ​获取 Bean 时​​:

    arduino 复制代码
    context.getBean(PayService.class); // 返回代理对象

💡 为什么这样设计?

  1. ​单一职责原则​​:

    • Spring 容器只管理"业务逻辑入口"(代理对象)
    • 原始对象作为实现细节被封装
  2. ​AOP 透明性保障​​:

    scss 复制代码
    // 使用者无需关心是否被代理
    payService.processPayment();
  3. ​资源优化​​:

    • 避免重复创建(代理对象复用原始对象)
    • 减少 Spring 容器管理对象数量

🛠 如何验证容器中的对象?

java 复制代码
// 1. 检查 Spring 上下文中的 Bean
PayService proxy = context.getBean(PayService.class);
System.out.println("Spring Bean 类型: " + proxy.getClass().getName());
// 输出: com.example.PayService$$EnhancerBySpringCGLIB$$...

// 2. 通过代理获取原始对象
if (proxy instanceof Advised) {
    Object target = ((Advised) proxy).getTargetSource().getTarget();
    System.out.println("原始对象类型: " + target.getClass().getName());
    // 输出: com.example.PayService
    
    System.out.println("是否相同对象: " + (proxy == target)); // 总是 false
}

⚠ 重要注意事项

  1. ​原始对象的访问限制​​:

    java 复制代码
    // 错误尝试:这样获取的仍然是代理对象
    @Autowired
    private PayService rawService; // 即使你认为是"原始对象"
  2. ​原型作用域(prototype)的特殊性​​:

    kotlin 复制代码
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public class PayService { ... }
    • 每次请求时:创建新原始对象 → 创建新代理对象
    • 但仍只暴露代理对象
  3. ​代理对象标识特点​​:

    比较方法 结果 原因说明
    proxy == raw false 本质不同对象
    proxy.equals(raw) true 代理重写了equals方法
    proxy.hashCode() 与raw相同 代理委托给原始对象实现
    proxy.getClass() 不同 代理是动态生成的子类/实现类

♋️生命周期示意图

markdown 复制代码
1. Spring容器启动
   ↓
2. 创建原始PayService实例(非Bean)
   ↓
3. 创建代理包装该原始实例
   ↓
4. 注册代理对象 → 成为Spring Bean "payService"
   ↓
5. @Autowired PayService → 注入代理对象

通过理解这个设计,就能明白为什么你看到的"两个对象"其实是一个容器的代理对象和它内部的非托管原始对象的关系。

🔄 生命周期交互图解

sequenceDiagram participant Container as Spring容器 participant Proxy as 代理对象 participant Target as 原始对象 Container->>Proxy: 创建代理对象 Proxy->>Target: 创建并持有原始对象 Container->>Proxy: 调用@PostConstruct Proxy->>Target: 委托调用初始化方法 Container->>Proxy: 调用业务方法 Proxy->>Target: 委托执行业务逻辑 Container->>Proxy: 调用@PreDestroy Proxy->>Target: 委托调用销毁方法

💎 结论

Spring 容器​​不会同时保存原始对象和代理对象作为独立 Bean​​。设计核心是:

  1. ​唯一入口​:只将代理对象注册为 Spring Bean
  2. ​内部封装​:原始对象作为代理的内部状态存在
  3. ​透明操作​:所有对 Bean 的操作都通过代理间接执行

这种设计保证了:

  • AOP 功能的无缝集成
  • 依赖注入的一致性
  • 资源管理的优化
  • 框架内部复杂性的屏蔽

如果您需要访问原始对象(通常不推荐),可以通过 AopUtils.getTargetObject(proxy)Advised.getTargetSource() 方法获取,但请记住这违反了AOP的设计哲学。

相关推荐
Boilermaker19921 分钟前
【Java EE】SpringIoC
前端·数据库·spring
写不出来就跑路7 分钟前
Spring Security架构与实战全解析
java·spring·架构
sleepcattt1 小时前
Spring中Bean的实例化(xml)
xml·java·spring
小七mod1 小时前
【Spring】Java SPI机制及Spring Boot使用实例
java·spring boot·spring·spi·双亲委派
ruan1145142 小时前
Java Lambda 类型推断详解:filter() 方法与 Predicate<? super T>
java·开发语言·spring·stream
paopaokaka_luck3 小时前
基于SpringBoot+Vue的非遗文化传承管理系统(websocket即时通讯、协同过滤算法、支付宝沙盒支付、可分享链接、功能量非常大)
java·数据库·vue.js·spring boot·后端·spring·小程序
邓不利东7 小时前
Spring中过滤器和拦截器的区别及具体实现
java·后端·spring
努力的小郑9 小时前
Spring三级缓存硬核解密:二级缓存行不行?一级缓存差在哪?
java·spring·面试
hello早上好9 小时前
Spring AOP:从代理创建到切点匹配
java·后端·spring
chanalbert9 小时前
从单体到微服务:Spring Cloud 开篇与微服务设计
spring boot·spring·spring cloud