Skip to content

Spring 解决循环依赖的核心机制:三级缓存 + 提前暴露对象

🔄 循环依赖场景

java

// BeanA 依赖 BeanB
@Component
public class BeanA {
    @Autowired
    private BeanB beanB;
}

// BeanB 依赖 BeanA
@Component
public class BeanB {
    @Autowired
    private BeanA beanA;
}

🔑 三级缓存结构(源码级解析)

缓存名称存储内容作用阶段
singletonObjects完全初始化后的单例Bean最终可用对象
earlySingletonObjects早期引用(未完成属性注入的对象)解决循环依赖
singletonFactoriesObjectFactory(Bean工厂)创建早期对象

🚀 解决流程(以 BeanA ↔ BeanB 为例)

  1. 创建 BeanA
    • 实例化空对象 new BeanA()
    • 将 BeanA 的 ObjectFactory 存入三级缓存 singletonFactories
    • 开始属性注入,发现需要 BeanB
  2. 创建 BeanB
    • 实例化空对象 new BeanB()
    • 将 BeanB 的 ObjectFactory 存入三级缓存
    • 开始属性注入,发现需要 BeanA
  3. 解决依赖
    • 从三级缓存获取 BeanA 的 ObjectFactory
    • 调用 getObject() 生成早期对象存入二级缓存 earlySingletonObjects
    • BeanB 完成初始化,移入一级缓存 singletonObjects
  4. 回溯处理
    • BeanA 获取到完全初始化的 BeanB
    • BeanA 完成初始化,移入一级缓存

⚠️ 关键限制条件

  1. 仅支持单例模式 原型(Prototype)作用域的Bean会直接抛出异常:

    java
    BeanCurrentlyInCreationException: Error creating bean with name 'beanA'...
  2. 构造器注入无法解决

    java
    // 这种场景会直接报错
    public BeanA(BeanB beanB) { this.beanB = beanB; }
    public BeanB(BeanA beanA) { this.beanA = beanA; }

💡 最佳实践方案

java
 
// 方案1:使用 @Lazy 延迟加载
public BeanA(@Lazy BeanB beanB) {
    this.beanB = beanB;
}

// 方案2:使用 setter 注入
public void setBeanB(BeanB beanB) {
    this.beanB = beanB;
}

🔍 源码关键方法

java
// AbstractAutowireCapableBeanFactory
protected Object doCreateBean(...) {
    // 1. 实例化对象
    instanceWrapper = createBeanInstance(...);
    
    // 2. 添加对象工厂到三级缓存(重要!)
    addSingletonFactory(beanName, () -> getEarlyBeanReference(...));
    
    // 3. 属性注入(可能触发依赖对象的创建)
    populateBean(...);
    
    // 4. 初始化完成
    initializeBean(...);
}

📊 性能优化建议

  • 避免超过两层的循环依赖
  • 监控 DefaultSingletonBeanRegistry 的缓存命中率
  • 对高频使用的Bean采用饿汉式加载(@DependsOn)

通过这种三级缓存机制,Spring 在保证性能的同时,优雅地解决了单例Bean的循环依赖问题。

文章来源于自己总结和网络转载,内容如有任何问题,请大佬斧正!联系我