​Spring 解决循环依赖的核心机制

Spring 通过 ​三级缓存 + 提前暴露对象引用 的方式解决单例 Bean 的循环依赖问题。以下是详细流程与原理分析:


一、循环依赖场景

假设存在两个 Bean 互相依赖:

@Component
public class A {
    @Autowired
    private B b;
}

@Component
public class B {
    @Autowired
    private A a;
}

二、三级缓存机制

Spring 容器通过以下三个缓存解决循环依赖:

缓存名称存储内容作用
singletonObjects完全初始化好的单例 Bean存放最终可用的 Bean
earlySingletonObjects早期引用(未完成属性注入的 Bean)避免重复创建早期对象
singletonFactoriesBean 的 ObjectFactory(工厂对象)生成早期引用(解决代理对象问题)

三、解决流程(以 A → B → A 为例)​
  1. 创建 Bean A

    • 实例化 A(调用构造器),生成原始对象 A@123
    • 将 A 的 ObjectFactory(用于生成早期引用)放入 singletonFactories
  2. 注入 A 的依赖

    • Spring 发现 A 依赖 B,开始创建 Bean B。
  3. 创建 Bean B

    • 实例化 B(调用构造器),生成原始对象 B@456
    • 将 B 的 ObjectFactory 放入 singletonFactories
  4. 注入 B 的依赖

    • Spring 发现 B 依赖 A,尝试从缓存获取 A:
      • Step 1:从 singletonObjects 获取 → 无。
      • Step 2:从 earlySingletonObjects 获取 → 无。
      • Step 3:从 singletonFactories 获取 A 的 ObjectFactory → 生成早期引用 A@123,将 A 存入 earlySingletonObjects,并从 singletonFactories 移除。
  5. 完成 B 的初始化

    • 将早期引用 A@123 注入到 B 中 → B@456.a = A@123
    • B 初始化完成,存入 singletonObjects,并从 earlySingletonObjects 和 singletonFactories 移除。
  6. 完成 A 的初始化

    • 将初始化后的 B@456 注入到 A 中 → A@123.b = B@456
    • A 初始化完成,存入 singletonObjects,并从 earlySingletonObjects 和 singletonFactories 移除。

四、关键条件与限制
  1. 仅支持单例 Bean

    • 原型(Prototype)作用域的 Bean 无法解决循环依赖,会直接抛出 BeanCurrentlyInCreationException
  2. 依赖注入方式

    • 支持 Setter 注入/字段注入:因为依赖注入发生在对象实例化之后。
    • 不支持构造器注入:若循环依赖的 Bean 均使用构造器注入,Spring 无法提前暴露对象引用,会导致初始化失败。
  3. AOP 代理的兼容性

    • 若循环依赖的 Bean 被 AOP 代理(如 @Async@Transactional),Spring 通过 singletonFactories 中的 ObjectFactory 生成代理对象的早期引用。

五、解决方案与代码示例
  1. 强制打破循环依赖(推荐)​

    • 重构代码,提取公共逻辑到第三个 Bean。
    @Component
    public class CommonService {
        // 公共逻辑
    }
  2. 使用 @Lazy 延迟加载

    • 在依赖注入时标记 @Lazy,延迟实际代理对象的创建。
    @Component
    public class A {
        @Autowired
        @Lazy  // 延迟注入 B 的代理对象
        private B b;
    }
  3. 改用 Setter 注入

    @Component
    public class A {
        private B b;
    
        @Autowired
        public void setB(B b) {  // Setter 注入
            this.b = b;
        }
    }

六、异常处理

若无法解决循环依赖,Spring 会抛出:

BeanCurrentlyInCreationException: Error creating bean with name 'a': 
Requested bean is currently in creation: Is there an unresolvable circular reference?

排查方法

  • 检查 Bean 的作用域(确保是单例)。
  • 检查是否使用了构造器注入。
  • 使用 @Lazy 或重新设计依赖关系。

总结

Spring 的三级缓存机制通过 ​提前暴露对象引用 解决了单例 Bean 的循环依赖问题,但需注意构造器注入和原型 Bean 的限制。优化代码结构、使用 @Lazy 或 Setter 注入是避免循环依赖的最佳实践。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值