Spring 的三级缓存与循环依赖详解
🧩 场景示例
@Component
class A {
@Autowired B b;
}
@Component
class B {
@Autowired A a;
}
🔁 Spring 如何解决循环依赖?
一、Spring 的三级缓存结构(在 DefaultSingletonBeanRegistry 中)
缓存层级 | 名称 | 说明 |
---|---|---|
一级缓存 | singletonObjects | 完全初始化的 Bean |
二级缓存 | earlySingletonObjects | 已创建但未填充依赖的 early Bean |
三级缓存 | singletonFactories | ObjectFactory 工厂,用于懒加载 Bean |
二、调用流程(当 B 注入 A 时)
Spring 调用:
getSingleton("a")
执行顺序:
- 查一级缓存 singletonObjects:❌
- 查二级缓存 earlySingletonObjects:❌
- 查三级缓存 singletonFactories:
- ✅ 存在 ObjectFactory
- 调用 getObject() → 得到 early A
- 放入二级缓存
- 移除三级缓存记录
三、A 的状态变迁
阶段 | A 的位置 |
---|---|
正在创建 A | 进入三级缓存(工厂) |
B 创建时注入 A | ObjectFactory.getObject() → early A |
early A 进入二级缓存 | 可用于依赖注入 |
A 初始化完成 → 一级缓存 | 进入 singletonObjects |
四、三级缓存 vs 二级缓存区别
对比维度 | 三级缓存 | 二级缓存 |
---|---|---|
内容 | ObjectFactory | early Bean 实例(可能未注入完成) |
是否立即初始化 | ❌ 否,懒加载 | ✅ 是,已触发初始化 |
生命周期 | 创建阶段 | 依赖注入阶段 |
消失时机 | 调用 getObject() 后立即移除 | Bean 初始化完成后移除 |
五、流程图(简化版)
创建 A → 加入三级缓存(工厂)
↓
A 注入 B → 创建 B
↓
B 注入 A → getSingleton("a") 查三级缓存
↓
执行 ObjectFactory.getObject() → 得到 early A → 存入二级缓存
↓
B 初始化完成 → 注入 A → A 完成初始化
↓
一级缓存:A 和 B 均完成
✅ 一句话总结
Spring 通过三级缓存机制,允许提前暴露 Bean 引用(early A),打破单例 Bean 的依赖闭环,实现字段/Setter 注入的循环依赖解决。
🧠 深度解析:B 注入 A 的过程中的三级缓存调用与状态变化
❓ 问题 1:怎么调用 ObjectFactory 获取 early A?
当 B 需要注入 A 时,Spring 执行如下逻辑:
/**
* 获取指定名称的单例 Bean 对象
* 支持从三级缓存机制中解析 early singleton(用于解决循环依赖)
*/
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 🔵【一级缓存】直接获取已完全初始化的单例对象
Object singletonObject = singletonObjects.get(beanName);
// 如果一级缓存没有,且当前 Bean 正在创建中(意味着可能存在循环依赖)
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 🟡【二级缓存】尝试从 earlySingletonObjects 中获取“早期暴露的 Bean 实例”
singletonObject = earlySingletonObjects.get(beanName);
// 如果二级缓存也没有,并且允许提前暴露(默认是 true)
if (singletonObject == null && allowEarlyReference) {
// 🔴【三级缓存】从 singletonFactories 获取创建早期 Bean 的 ObjectFactory
ObjectFactory<?> singletonFactory = singletonFactories.get(beanName);
if (singletonFactory != null) {
// 🔥 关键调用:通过 ObjectFactory 创建 early singleton(A 的半成品)
singletonObject = singletonFactory.getObject();
// 👉 把 early A 放入二级缓存,供依赖注入使用
earlySingletonObjects.put(beanName, singletonObject);
// ❌ 从三级缓存中移除 A 的工厂,避免重复创建 early A
singletonFactories.remove(beanName);
}
}
}
// 返回最终获取到的 Bean(可能是一级缓存的完整 Bean,也可能是二级缓存中的 early Bean)
return singletonObject;
}
✔️ singletonFactory.getObject()
触发了 A
的 early reference 实例创建。
❓ 问题 2:A 从三级缓存进入二级缓存,发生了什么变化?
缓存状态 | 内容 |
---|---|
三级缓存 | ObjectFactory(尚未创建 A) |
二级缓存 | early A 对象实例(未注入完) |
- 原本只在三级缓存中存在一个 懒加载工厂;
- 调用
getObject()
后,创建了 early A(半成品对象); - 把这个 early A 放进二级缓存,供 B 注入;
- 同时从三级缓存中将
ObjectFactory
移除,避免重复创建。
❓ 问题 3:三级缓存和二级缓存的核心区别?
属性 | 三级缓存(singletonFactories) | 二级缓存(earlySingletonObjects) |
---|---|---|
存储内容 | ObjectFactory(工厂) | early Bean 实例(真实对象) |
是否已实例化对象 | ❌ 否,懒加载 | ✅ 是,已构造未注入完 |
使用时机 | 注入前的延迟暴露机制 | 实际注入引用依赖时 |
生命周期 | 调用 getObject() 后立即删除 | Bean 初始化完成后删除 |
✅ 总结
Spring 会在 Bean A 尚未初始化完成时,通过 ObjectFactory 提前暴露其引用,注入给 B 使用,
这个 early A 就从 三级缓存 → 二级缓存,实现了单例 Bean 的“半成品依赖注入”。