Spring的三级缓存介绍
Spring框架在处理单例Bean的创建和依赖注入时,使用了三级缓存机制来管理Bean的生命周期和解决潜在的循环依赖问题。这些缓存主要定义在org.springframework.beans.factory.support.DefaultSingletonBeanRegistry类中。下面结合源码进行详细介绍。
- 三级缓存的定义和作用
Spring的三级缓存是三个Map结构,用于存储不同阶段的Bean实例或工厂对象。它们分别是:
- 一级缓存(singletonObjects):存储完全初始化的单例Bean实例。这是最终的缓存,用于存放已经完成实例化、属性注入和初始化后的Bean。
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);作用:提供给外部直接获取的成熟Bean。一旦Bean放入这里,后续的getBean调用就会直接返回它。
- 二级缓存(earlySingletonObjects):存储早期的Bean实例,即已经实例化但尚未完成属性注入和初始化的Bean引用。主要用于临时存储从三级缓存生成的早期对象,避免重复创建。
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);作用:在循环依赖场景中,提供一个半成品Bean引用给依赖方使用。
- 三级缓存(singletonFactories):存储ObjectFactory对象,这些工厂用于生成早期的Bean引用(可能包括代理对象)。
/** Creation-time registry of singleton factories: bean name to ObjectFactory. */
private final Map<String, Object> singletonFactories = new ConcurrentHashMap<>(16);作用:允许在Bean创建过程中提前暴露一个工厂,当发生循环依赖时,通过调用工厂的getObject()方法生成早期Bean引用,从而打破循环。
这些缓存的设计确保了Bean创建的线程安全性和一致性,通过synchronized锁(在代码中体现为singletonLock)来控制并发访问。raw.githubusercontent.com
- 三级缓存的工作流程(结合getSingleton方法)
Spring在获取或创建Bean时,主要通过DefaultSingletonBeanRegistry的getSingleton方法来操作这些缓存。getSingleton有多个重载版本,这里重点分析两个关键实现:
- getSingleton(String beanName, boolean allowEarlyReference):用于获取Bean,支持早期引用。 这个方法首先尝试从一级缓存中获取,如果Bean正在创建中(通过isSingletonCurrentlyInCreation(beanName)判断),则依次检查二级和三级缓存。
protected @Nullable Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock.
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) { // 使用锁确保线程安全
// Consistent creation of early reference within full singleton lock.
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject(); // 调用工厂生成早期Bean
this.earlySingletonObjects.put(beanName, singletonObject); // 放入二级缓存
this.singletonFactories.remove(beanName); // 移除三级工厂
}
}
}
}
}
}
return singletonObject;
}流程:一级 → (如果在创建中) 二级 → (如果允许早期引用) 加锁后从三级工厂生成并移到二级。
- getSingleton(String beanName, ObjectFactory singletonFactory):用于创建Bean,如果不存在则调用提供的factory创建。 这个方法在创建过程中会调用singletonFactory.getObject()来实际实例化Bean,创建完成后调用addSingleton(beanName, singletonObject)将Bean放入一级缓存,并清理二级和三级缓存。
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// ... (检查销毁、beforeSingletonCreation等)
try {
singletonObject = singletonFactory.getObject(); // 执行创建
newSingleton = true;
}
// ... (异常处理)
finally {
// ... (afterSingletonCreation)
}
if (newSingleton) {
addSingleton(beanName, singletonObject); // 放入一级,清理二级三级
}
}
return singletonObject;
}
}addSingleton方法:
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}这些方法确保了缓存的层级流动:从三级(工厂)生成早期对象到二级(临时存储),最终到一级(成熟对象)。
- 三级缓存如何解决循环依赖(结合doCreateBean方法)
循环依赖是指两个或多个Bean相互依赖,例如A依赖B,B依赖A。在Spring中,这种问题通过三级缓存和早期暴露机制来解决。主要逻辑在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory的doCreateBean方法中实现。
- 早期暴露的时机:在Bean实例化后(createBeanInstance)、但属性注入(populateBean)前,如果是单例且允许循环引用(allowCircularReferences默认为true)且正在创建中,则添加三级工厂。
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if(earlySingletonExposure){
if(logger.
isTraceEnabled()){
logger.
trace("Eagerly caching bean '"+beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () ->
getEarlyBeanReference(beanName, mbd, bean)); // 添加三级工厂
}这里addSingletonFactory将一个Lambda工厂放入singletonFactories,工厂调用getEarlyBeanReference生成早期引用(可能应用AOP代理)。
- getEarlyBeanReference的实现:
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
exposedObject = bp.getEarlyBeanReference(exposedObject, beanName); // 可能创建代理
}
}
return exposedObject;
}解决循环依赖的过程: 假设A依赖B,B依赖A: 创建A:实例化A后,添加A的工厂到三级缓存,然后populate A(注入B)。 注入B时,发现B未创建,创建B:实例化B后,添加B的工厂到三级缓存,然后populate B(注入A)。 注入A时,发现A正在创建中(isSingletonCurrentlyInCreation=true),于是从缓存获取A:一级无 → 二级无 → 从三级工厂获取A的早期引用(半成品),放入二级缓存。 B完成populate,使用A的早期引用完成初始化,放入一级缓存。 返回到A,继续populate,使用完整的B完成初始化,放入一级缓存(同时清理二级三级)。
如果不允许循环引用(allowCircularReferences=false),则不会添加工厂,检测到循环时抛出BeanCurrentlyInCreationException。
注意:三级缓存是必需的,因为二级只能存储裸Bean,而三级工厂允许在生成早期引用时应用后处理器(如AOP代理),确保一致性。如果只用二级,代理Bean的循环依赖会失败。raw.githubusercontent.com
- 是否使用三级缓存解决了循环依赖的问题?
是的,Spring正是通过三级缓存机制解决了单例Bean的循环依赖问题。这种设计允许在Bean创建过程中提前暴露引用,避免无限递归,同时支持AOP等扩展。需要注意的是:
- 只适用于单例作用域(prototype不支持循环依赖)。
- 如果涉及构造函数注入的循环依赖,无法解决(因为实例化前无法暴露)。
- 可以手动关闭循环依赖支持(setAllowCircularReferences(false)),但默认开启。
通过以上源码分析,可以看到三级缓存是Spring IoC容器核心的一部分,确保了依赖注入的可靠性和灵活性。
通过邮件回复




