day07-Spring循环依赖
前言:
项目启动时候,可能会存在A创建依赖B,B创建依赖A,这样就出现了循环依赖。
摘要:
Spring通过三级缓存机制解决循环依赖问题。一级缓存存储完整初始化的单例Bean,二级缓存存储半成品Bean,三级缓存存储ObjectFactory用于生成早期引用。当检测到循环依赖时,Spring会通过三级缓存获取早期引用并存入二级缓存,避免重复创建。该机制还支持AOP代理,确保在循环依赖时也能正确创建代理对象。核心流程包括实例化后提前暴露ObjectFactory、三级缓存的添加与查询逻辑,以及早期引用的处理,从而保证Bean创建过程的一致性和正确性。
流程图:

1、三级缓存的定义
c
// 一级缓存:存储完全初始化好的单例bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 二级缓存:存储提前暴露(已经初始化,但是没有实例化)
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
// 三级缓存:存储ObjectFactory,用于生成早期Bean的引用
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
1、singletonObjects :缓存经过了完整生命周期的bean
2、earlySingletonObjects:缓存未经过完整生命周期的bean,
2.1 如果某个bean出现了循环依赖,就会提前把暂时未经过完整生命周期的bean放入earlySingletonObjects中,
2.2 这个bean如果经过AOP那么就会把代理对象放入earlySingletonObjects中,否则就把原始对象放入earlySingletonObjects中。
3、singletonFactories:缓存一个ObjectFactory,也是 Lambda表达式。每个Bean的生成过程中,经过实例化得到一个原始对象后,都会提前暴露一个Lambda表达式,并保存到三级缓存中。
3.1 三级缓存可能用到,也可能用不到。
3.2 如果当前Bean没有出现循环依赖,那么这个Lambda用不到,会正常执行完生成完整的Bean放入 singletonObjects中,
3.3 如果bean在依赖注入时出现了循环依赖,从三级缓存拿到一个对象,放入二级缓存中,并根据beanName从三级缓存中删除。
4、earlyProxyReferences :主要用来记录原始对象是否进行AOP。
2、三级缓存的作用
| 缓存级别 | 名称 | 存储内容 | 作用 |
|---|---|---|---|
| 一级缓存 | singletonObjects | 完全初始化好的Bean | 提供最终单例bean |
| 二级缓存 | earlySingletonObjects | 半成品 | 避免重复创建早期引用 |
| 三级缓存 | singletonFactories | ObjectFactory | 创建早期Bean,支持AOP代理 |
3、核心代码讲解
3.1 实例化后提前暴露,在Bean的实例化后,属性填充前
1)Spring会判断该Bean单例允许循环引用,将会创建一个返回该Bean的当前的原始对象 ObjectFactory 存入三级缓存中。
2)处理循环依赖,当一个Bean 在填充属性发现依赖另一个 Bean的时候,这时会调用getEarlyBeanReference() 方法,遍历所有的SmartInstantiationAwareBeanPostProcessor ,给后处理器一个机会,返回早期引用。
c
// org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java:596
// 单例&&是否是当前正在创建的bean
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));
}
3.2 三级缓存添加的逻辑
synchronized (this.singletonObjects) 先加入锁原因,是保证多线程的缓存操作下的一致性,防止同一个Bean被重复创建
c
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
// 一级缓存不存在情况下
if (!this.singletonObjects.containsKey(beanName)) {
// 先添加三级缓存,再根据beanName删除二级缓存中的半成品bean
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
3.3 获取单例bean的缓存查询
c
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 1.检查一级缓存,如果有直接返回
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 2.从二级缓存中获取
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// 双重检查
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
// 3.从三级缓存获取,放入二级缓存, 并删除三级缓存
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
3.4 早期引用
// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#getEarlyBeanReference
c
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;
}
3.5 AOP代理创建
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#getEarlyBeanReference
- 时机:
- AOP 代理器(AbstractAutoProxyCreator) 在getEarlyBeanReference方法就会工作 在循环依赖发生时就提前创建代理对象,而不是等待初始化之后。
- 保证一致性:
- 调用 getEarlyBeanReference() 方法内部会先记录该Bean earlyProxyReferences,postProcessAfterInitialization 方法里会判断该Bean是否已经提前代理过了,如果代理过则不再重复处理,确保整个容器中对于同一个Bean,循环依赖时拿到和最终持有的都是同一个代理对象。【org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization】
c
// org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization
// 方法里会判断该Bean是否已经提前代理过了,如果代理过则不再重复处理,确保整个容器中对于同一个Bean,循环依赖时拿到和最终持有的都是同一个代理对象
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
// 记录原始bean,避免重复代理
this.earlyProxyReferences.put(cacheKey, bean);
// 如果这个bean经过AOP,那么把这个代理对象放入 earlySingletonObjects,否则把原始对象放入里面
return wrapIfNecessary(bean, beanName, cacheKey);
}
4、三级缓存的设计思想以及常见面试题
问题一:二级缓存能否去掉
不能,二级缓存作用
- 避免重复执行ObjectFactory.getObject()(性能考虑)
- 保证同一个 Bean在循环依赖中返回相同的早期引用。
问题二:添加三级缓存bean的引用和获取singleton bean 添加 synchronized的作用是什么?
c
synchronized (this.singletonObjects) {
// 1.保证多线程环境下,缓存操作的一致性
// 2.防止同一个bean被重复创建
}
位置如下:
获取bean的位置
c
// org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)
protected 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();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
添加三级缓存的地方:
c
// org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
问题三:在使用Spring如果避免多级缓存带来的复杂性
通过清晰的缓存升级策略
- 创建中:三级缓存
- 早期引用:二级缓存
- 完整体:一级缓存
- 状态时不可逆,只能向上一季流动。
问题四:为什么必须是三级缓存?
- 三级缓存:singletonFactories 核心是(ObjectFactory)的能力,封装了复杂的后处理器(如getEarlyBeanReference()) ,可以按需生成,可能被包装过的对象(如代理)。这是 一个"懒加载"过程,只在发生循环依赖时才调用。
- 二级缓存:earlySingletonObjects,半成品暂存区,一旦通过通过缓存的工厂生产出对象, 就放入二级缓存,下次需要时直接获取,避免重复执行工厂逻辑。
- 如果只是二级缓存:
- 那么就需要Bean实例化后立即执行所有可能的AOP代理后处理逻辑来创建对象,这样违反了Bean生命周期的设计原则,而且对于没有循环依赖的Bean,这种"提前代理"是不必要的开销。
喜欢我的文章记得点个在看,或者点赞,持续更新中ing...