Spring 源码学习-理解三级缓存
大约 3 分钟
合图片内容和 Spring 源码的三级缓存设计,我将通过 分层解析 和 流程推演 的方式,帮助你彻底理解 Spring 如何通过三级缓存解决循环依赖问题,并解释图中提到的“三级改二级”的可行性。

1. 三级缓存的核心设计
Spring 在 DefaultSingletonBeanRegistry
类中定义了三个关键缓存:
// 一级缓存:存放完整初始化后的单例 Bean(成品)
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 二级缓存:存放提前暴露的 Bean(半成品,未完成属性注入和初始化)
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
// 三级缓存:存放 Bean 的 ObjectFactory(用于处理代理对象的延迟生成)
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
2. 循环依赖解决流程(以 A→B→A 为例)
步骤 1:创建 Bean A
- 实例化 A:通过反射调用构造器创建对象
A@1234
(此时属性未填充)。 - 暴露 ObjectFactory:将 A 的 ObjectFactory 存入三级缓存(图中提到的
addSingletonFactory
方法)。
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
步骤 2:填充 A 的属性时发现依赖 B
- 触发 B 的创建:调用
getBean("B")
,进入 B 的创建流程。 - 实例化 B:创建对象
B@5678
,并暴露 B 的 ObjectFactory 到三级缓存。
步骤 3:填充 B 的属性时再次请求 A
- 从三级缓存获取 A:调用
getSingleton("A")
,通过三级缓存中的 ObjectFactory 生成 A 的早期引用(可能触发 AOP 代理)。
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 调用 getEarlyBeanReference 生成代理对象(如果需要)
earlySingletonObjects.put(beanName, singletonFactory.getObject());
singletonFactories.remove(beanName);
}
- 将 A 的早期引用存入二级缓存:此时
earlySingletonObjects
中存有A@Proxy
(代理对象)。
步骤 4:完成 B 的创建
- B 初始化完成:B 成为完整对象,存入一级缓存
singletonObjects
。
步骤 5:回到 A 的创建流程
- 继续填充 A 的属性:注入已初始化的 B。
- A 初始化完成:存入一级缓存,删除二级缓存中的早期引用。
3. 为何需要三级缓存?(关键难点)
- 代理对象的延迟生成:如果 A 需要 AOP 代理,三级缓存的 ObjectFactory 能延迟生成代理对象,直到真正发生循环依赖时才创建(避免无意义的代理)。
- 二级缓存直接暴露半成品的风险:若只有二级缓存,可能返回未经代理的原始对象(破坏 AOP 特性)。
4. 图中提到的“三级改二级”是否可行?
方法 1:修改 doCreateBean 中的代码
- 直接暴露半成品到二级缓存:
// 原代码:addSingletonFactory(三级缓存)
// 修改为:
earlySingletonObjects.put(beanName, bean);
- 风险:如果 Bean 需要代理,会导致后续注入的是原始对象而非代理对象。
方法 2:移除三级缓存,合并到二级缓存
- 调整 getSingleton 逻辑:
// 原三级缓存逻辑
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
object = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, object);
this.singletonFactories.remove(beanName);
}
// 改为直接从二级缓存获取
object = this.earlySingletonObjects.get(beanName);
- 代价:必须提前生成代理对象(无论是否发生循环依赖),牺牲了按需代理的灵活性。
5. 从图片文字看 Spring 源码的关键点
getEarlyBeanReference
** 方法**:生成早期引用,处理 AOP 代理(见AbstractAutowireCapableBeanFactory
)。doCreateBean
** 中的 addSingletonFactory**:暴露 ObjectFactory 到三级缓存的入口。- 循环依赖的闭环逻辑:通过缓存层级跳转避免无限递归(如
A→B→A
的依赖链)。
6. 总结:三级缓存的核心价值
缓存层级 | 作用 | 设计意义 |
---|---|---|
一级缓存(成品) | 存放完整 Bean | 保证单例完整性 |
二级缓存(半成品) | 存放提前暴露的 Bean(可能代理) | 快速响应循环依赖请求 |
三级缓存(工厂) | 延迟生成代理对象 | 按需代理,避免无效对象创建 |
通过三级缓存的协作,Spring 在性能(减少代理生成)、正确性(AOP 兼容)和扩展性(支持多种后处理器)之间取得了平衡。