spring循环依赖

Spring 通过三级缓存机制解决单例 Bean 的循环依赖问题,其核心思想是提前暴露未完全初始化的 Bean 引用。以下是详细流程和原理:


1. 循环依赖的场景

假设两个 Bean 相互依赖:

  • BeanA 依赖 BeanB
  • BeanB 依赖 BeanA

如果没有特殊处理,Spring 会在创建 BeanA 时发现需要 BeanB,而创建 BeanB 时又需要 BeanA,导致死循环。


2. 三级缓存的作用

Spring 使用三个 Map(缓存)管理 Bean 的中间状态:

  1. 一级缓存 singletonObjects
    存放完全初始化好的单例 Bean,可直接使用。
  2. 二级缓存 earlySingletonObjects
    存放早期暴露的 Bean 对象(已实例化但未完成属性填充和初始化)。
  3. 三级缓存 singletonFactories
    存放 Bean 工厂对象ObjectFactory),用于生成早期 Bean 的引用(可能包含代理对象)。

3. 解决循环依赖的流程

BeanABeanB 相互依赖为例:

步骤 1:创建 BeanA
  1. 实例化 BeanA
    调用构造函数创建 BeanA 的实例(此时属性未填充)。
  2. 暴露早期引用
    BeanA 的工厂对象(ObjectFactory)放入三级缓存
  3. 填充属性 BeanB
    Spring 发现 BeanA 依赖 BeanB,触发 BeanB 的创建。
步骤 2:创建 BeanB
  1. 实例化 BeanB
    调用构造函数创建 BeanB 的实例。
  2. 暴露早期引用
    BeanB 的工厂对象放入三级缓存
  3. 填充属性 BeanA
    Spring 发现 BeanB 依赖 BeanA,尝试从缓存中获取 BeanA
    • 一级缓存:无。
    • 二级缓存:无。
    • 三级缓存:找到 BeanA 的工厂对象,调用 getObject() 生成早期引用,并将 BeanA 移至二级缓存
  4. 完成 BeanB 的初始化
    BeanB 的完整对象放入一级缓存
步骤 3:回到 BeanA 的初始化
  1. 获取 BeanB 实例
    BeanA 通过已初始化的 BeanB 完成属性注入。
  2. 完成 BeanA 的初始化
    BeanA 放入一级缓存,并从二级缓存中移除。

4. 关键设计点

为什么需要三级缓存?
  • 分离职责
    三级缓存通过 ObjectFactory 延迟生成 Bean 的早期引用,确保 AOP 代理仅在需要时创建(例如,若 BeanA 需要代理,工厂会返回代理对象,避免重复创建)。
  • 避免重复代理
    如果只有二级缓存,可能在多个地方重复生成代理对象,破坏单例。
为何构造函数注入无法解决循环依赖?
  • 实例化前无法暴露引用
    构造函数注入发生在实例化阶段,此时 Bean 尚未创建,无法提前暴露引用到三级缓存,导致 Spring 抛出 BeanCurrentlyInCreationException

5. 限制条件

  1. 仅支持单例 Bean
    原型(Prototype)作用域的 Bean 每次创建新对象,无法通过缓存解决循环依赖。
  2. 依赖的注入方式必须为 Setter 或字段注入
    构造函数注入无法解决循环依赖。

6. 总结

Spring 通过三级缓存提前暴露未初始化的 Bean 引用,结合延迟代理生成,解决了单例 Bean 的循环依赖问题。其核心是空间换时间,在保证单例和 AOP 一致性的前提下,避免死循环。理解这一机制有助于在开发中避免循环依赖陷阱,或在必要时调整依赖注入方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值