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 | 早期引用(未完成属性注入的对象) | 解决循环依赖 |
singletonFactories | ObjectFactory(Bean工厂) | 创建早期对象 |
🚀 解决流程(以 BeanA ↔ BeanB 为例)
- 创建 BeanA
- 实例化空对象
new BeanA()
- 将 BeanA 的 ObjectFactory 存入三级缓存
singletonFactories
- 开始属性注入,发现需要 BeanB
- 实例化空对象
- 创建 BeanB
- 实例化空对象
new BeanB()
- 将 BeanB 的 ObjectFactory 存入三级缓存
- 开始属性注入,发现需要 BeanA
- 实例化空对象
- 解决依赖
- 从三级缓存获取 BeanA 的 ObjectFactory
- 调用
getObject()
生成早期对象存入二级缓存earlySingletonObjects
- BeanB 完成初始化,移入一级缓存
singletonObjects
- 回溯处理
- BeanA 获取到完全初始化的 BeanB
- BeanA 完成初始化,移入一级缓存
⚠️ 关键限制条件
仅支持单例模式 原型(Prototype)作用域的Bean会直接抛出异常:
javaBeanCurrentlyInCreationException: Error creating bean with name 'beanA'...
构造器注入无法解决
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的循环依赖问题。