目录
在 Spring 框架中,循环依赖(Circular Dependency)是开发过程中最常见的陷阱之一。Spring 通过三级缓存机制优雅地解决了这一问题,但许多开发者会疑惑:为什么需要三级缓存?二级缓存能否实现同样的效果?本文将通过源码分析、场景推演和设计哲学,揭示这一机制背后的精妙设计。
一. 循环依赖的本质矛盾
1. 什么是循环依赖?
当两个或多个 Bean 相互依赖时,就会形成循环依赖:
// Bean A 依赖 Bean B
@Component
public class A {
@Autowired
private B b;
}
// Bean B 依赖 Bean A
@Component
public class B {
@Autowired
private A a;
}
2. 矛盾的核心
构造器注入无法解决循环依赖:必须通过 Setter 或字段注入。
Bean 创建的生命周期冲突:
二. 三级缓存架构解析
Spring 通过三级缓存解决循环依赖问题,其核心数据结构如下:
缓存级别 | 数据结构 | 存储内容 |
---|---|---|
一级缓存 | singletonObjects |
完全初始化完成的单例 Bean |
二级缓存 | earlySingletonObjects |
提前暴露的早期 Bean(未完成属性注入) |
三级缓存 | singletonFactories |
Bean 的 ObjectFactory(用于生成代理对象) |
三级缓存工作流程图
三、为什么必须三级缓存?
1. 二级缓存的致命缺陷
假设只有一级缓存 + 二级缓存:
场景:Bean A 依赖 Bean B,Bean B 依赖 Bean A,且 A 需要 AOP 代理
问题链:
A 实例化后放入二级缓存(原始对象)
B 从二级缓存获取 A 的原始对象并注入
A 完成初始化后需要生成代理对象
结果:B 中持有的是 A 的原始对象,与最终代理对象不一致
2. 三级缓存的精妙设计
通过 ObjectFactory
延迟处理代理:
// AbstractAutowireCapableBeanFactory
protected Object getEarlyBeanReference(String beanName, Object bean) {
// 通过后置处理器生成代理对象
return applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
动态决策:只有在发生循环依赖时才会调用 ObjectFactory
代理一致性:保证所有依赖方拿到的是同一个代理对象
四、场景推演:三级缓存如何解决代理问题
1. 正常流程(无循环依赖)
2. 循环依赖流程 
五、源码级验证
1. 三级缓存获取逻辑
// DefaultSingletonBeanRegistry
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
2. 代理对象生成点
// AbstractAutowireCapableBeanFactory
protected Object doCreateBean(...) {
// 1. 实例化对象
instanceWrapper = createBeanInstance(beanName, mbd, args);
// 2. 加入三级缓存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
// 3. 属性填充(可能触发循环依赖)
populateBean(beanName, mbd, instanceWrapper);
// 4. 初始化(生成最终代理对象)
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
六、如果强行使用二级缓存?
假设合并三级缓存中的 singletonFactories
和 earlySingletonObjects
,会导致:
提前暴露未完成对象:可能将半成品 Bean 暴露给其他线程
代理对象不一致:普通 Bean 与代理 Bean 可能同时存在
内存泄漏风险:无法及时清理创建失败的 Bean 引用