Spring 的 三级缓存(Three-level Cache) 主要应用在 Spring Bean 的实例化 过程中,用于 解决循环依赖。这是 Spring IoC 容器的核心机制之一,特别是在 单例模式(Singleton Scope) 下。
Spring 三级缓存
Spring 在 DefaultSingletonBeanRegistry
中定义了三级缓存:
-
singletonObjects(一级缓存):存放 完全实例化的 Bean,即 创建完成且初始化完成 的 Bean。
-
earlySingletonObjects(二级缓存):存放 创建完成但未进行初始化的 Bean,主要用于解决 循环依赖。
-
singletonFactories(三级缓存):存放 Bean 的 ObjectFactory,用于在必要时 提前曝光 Bean 实例,解决代理对象的循环依赖。
三级缓存的作用
主要作用是:
-
避免重复创建 Bean,提升 Bean 复用率。
-
解决循环依赖(特别是 AOP 代理场景)。
-
优化性能,减少不必要的锁竞争。
Spring 三级缓存源码解析
核心逻辑在 AbstractAutowireCapableBeanFactory#doCreateBean()
方法中,简化流程如下:
1. 创建 Bean 实例
// 获取 bean 的定义信息
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// 创建 bean 实例(只创建对象,不初始化)
Object beanInstance = createBeanInstance(beanName, mbd, args);
此时,Bean 只是被实例化,但尚未初始化。
2. 放入三级缓存(singletonFactories)
// 提前暴露一个 ObjectFactory(用于 AOP 代理)
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, beanInstance));
-
addSingletonFactory()
:将 ObjectFactory 放入 三级缓存singletonFactories
。 -
getEarlyBeanReference()
:如果有 AOP 代理,这里会返回代理对象。
3. 解决循环依赖(从三级缓存提升到二级缓存)
若其他 Bean 依赖此 Bean,则会尝试从缓存获取:
// 尝试从二级缓存 earlySingletonObjects 获取实例
earlySingleton = this.earlySingletonObjects.get(beanName);
if (earlySingleton == null) {
// 若二级缓存没有,则调用 ObjectFactory 从三级缓存创建 Bean
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
earlySingleton = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, earlySingleton);
this.singletonFactories.remove(beanName);
}
}
-
如果一个 Bean 依赖另一个未初始化的 Bean,Spring 会先去二级缓存
earlySingletonObjects
取实例。 -
如果二级缓存没有,则会从 三级缓存
singletonFactories
中调用ObjectFactory#getObject()
创建实例,并存入二级缓存。 -
最后从三级缓存移除,避免重复创建。
4. 完成 Bean 初始化,移入一级缓存
// Bean 初始化完成后,放入一级缓存,并移除二级缓存
this.singletonObjects.put(beanName, bean);
this.earlySingletonObjects.remove(beanName);
-
一级缓存
singletonObjects
存放 完全初始化的 Bean,用于最终获取实例。
Spring 三级缓存解决循环依赖的示例
循环依赖场景
@Component
public class A {
@Autowired
private B b;
}
@Component
public class B {
@Autowired
private A a;
}
A
依赖 B
,B
依赖 A
,如果没有三级缓存,会导致 StackOverflowError
。
三级缓存的作用
-
Spring 发现
A
需要B
,开始创建A
。 -
在实例化
A
后,将A
放入三级缓存(但尚未初始化)。 -
Spring 发现
A
依赖B
,开始创建B
。 -
在实例化
B
后,发现B
依赖A
,从三级缓存获取A
并注入。 -
B
完成创建,并初始化A
,最终A
和B
都被放入一级缓存。
为什么 Spring 需要三级缓存?
如果只有二级缓存,无法解决 AOP 代理的循环依赖问题!
-
二级缓存(earlySingletonObjects) 只能存 原始对象,但 AOP 需要代理对象。
-
三级缓存(singletonFactories) 允许通过
getEarlyBeanReference()
生成代理对象。 -
当 AOP 代理存在时,Spring 直接返回代理对象,保证最终
A
和B
的依赖一致。
总结
-
一级缓存
singletonObjects
:存放 完全初始化的 Bean,最终可用的单例对象。 -
二级缓存
earlySingletonObjects
:存放 实例化但未初始化的 Bean,用于解决普通循环依赖。 -
三级缓存
singletonFactories
:存放 ObjectFactory,用于 AOP 代理或特殊情况。
三级缓存的作用
✅ 提高 Bean 复用率,避免重复创建
✅ 解决 Spring 单例 Bean 的循环依赖问题
✅ 支持 AOP 代理的提前曝光,保证代理对象的正确注入