Appearance
Spring 循环依赖
本文介绍Spring中有关循环依赖的问题。
1. 循环依赖流程讲解
Spring使用三级缓存解决循环依赖:
- 一级缓存
singletonObjects:存放已创建完成,已依赖注入的Bean; - 二级缓存
earlySingletonObjects:存放已创建完成,但是未完成依赖注入的Bean; - 三级缓存
singletonFactories:存放ObjectFactory,可以通过getObject()获取Bean;

当我们从容器中获取Bean时,会先检查三级缓存中有没有Bean:
- 先检查一级缓存中有没有Bean,如果有,直接返回;如果没有,进行下一步;
- 再检查二级缓存中有没有Bean,如果有,直接返回;如果没有,进行下一步;
- 最后检查三级缓存中有没有Bean
- 如果有,从三级缓存中获取
ObjectFactory,调用getObject()方法获取Bean,然后将这个Bean放入二级缓存,将这个ObjectFactory从三级缓存中删除; - 如果没有,返回
null;
- 如果有,从三级缓存中获取
Spring中的循环依赖解决流程,首先以下面的代码为例:
java
@Component
class A{
@Autowired
private B b;
}
@Component
class B{
@Autowired
private A a;
}假设现在要从容器中获取A实例对象:
首先,容器中没有A对象,通过反射创建出A对象,注意,此时A对象中的属性b为null;
然后,将A对象封装为
ObjectFactory,放入三级缓存singletonFactories中;
然后,开始对A对象进行依赖注入,发现需要B对象;
在容器中没有B没有,通过反射创建出B对象,同样地,此时B对象中的属性a为null;
同样将B对象封装为
ObjectFactory,放入三级缓存singletonFactories中;
然后,开始对B对象进行依赖注入;
发现B对象依赖A对象,此时从容器中获取A对象;
遵循前面的查找规则,最终A对象从三级缓存移到二级缓存;

最后,B对象的创建和依赖注入流程完成,此时B对象创建完成,最后,会将B对象放入一级缓存,并且把B对象从二级、三级缓存中删除;

当经过B对象的创建过程后,容器中就有B对象了,此时可以在一级缓存中找到,将其注入到A对象中;
最后,A对象创建完成,将A对象放入一级缓存,并且将A对象从二级、三级缓存中删除;

2. 循环依赖源码分析
2.1 从三级缓存中查找Bean
从三级缓存中查找Bean的方法是getSingleton():
txt
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)源码如下:
java
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 首先从一级缓存中查找Bean
Object singletonObject = this.singletonObjects.get(beanName);
// 在一级缓存中为空,并且当前BeanName所对应的Bean是在创建过程中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 从二级缓存中获取Bean
singletonObject = this.earlySingletonObjects.get(beanName);
// 在二级缓存中为空,并且允许获取Bean的早期引用(allowEarlyReference为true)
if (singletonObject == null && allowEarlyReference) {
// 加锁
if (!this.singletonLock.tryLock()) {
return null;
}
try {
// 为了防止其他线程创建Bean,加锁后需要再判断一下
// 先查找一级缓存
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) {
// 在三级缓存中找到了,调用getObject()方法获取Bean
singletonObject = singletonFactory.getObject();
// 从三级缓存中删除ObjectFactory
if (this.singletonFactories.remove(beanName) != null) {
// 加入到二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
}
else {
// 如果从三级缓存中删除ObjectFactory失败,表示可能有其他线程在锁外修改了singletonFactories 和 singletonObjects
// 再次判断是为了确保当前线程拿到的是最新状态下的数据,避免因并发导致的数据不一致问题。
singletonObject = this.singletonObjects.get(beanName);
}
}
}
}
}
finally {
// 释放锁
this.singletonLock.unlock();
}
}
}
// 返回获取到的Bean
return singletonObject;
}2.2 将Bean加入到一级缓存中
将Bean加入到一级缓存的方法是addSingleton():
txt
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingleton源码如下:
java
protected void addSingleton(String beanName, Object singletonObject) {
// 将Bean加入到一级缓存中,singletonObject就是Bean
Object oldObject = this.singletonObjects.putIfAbsent(beanName, singletonObject);
if (oldObject != null) {
// 重复加入,报错
throw new IllegalStateException("Could not register object [" + singletonObject +
"] under bean name '" + beanName + "': there is already object [" + oldObject + "] bound");
}
// 从三级缓存中移除
this.singletonFactories.remove(beanName);
// 从二级缓存中移除
this.earlySingletonObjects.remove(beanName);
// 已注册的Bean记录
this.registeredSingletons.add(beanName);
Consumer<Object> callback = this.singletonCallbacks.get(beanName);
if (callback != null) {
callback.accept(singletonObject);
}
}2.3 创建Bean
创建Bean的关键方法:
txt
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean主要逻辑如下:
- 创建Bean:
java
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance();- 将创建的Bean包装为ObjectFactory,放入三级缓存:
java
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}- 依赖注入与初始化
java
Object exposedObject = bean;
try {
// 依赖注入
populateBean(beanName, mbd, instanceWrapper);
// 初始化
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException bce && beanName.equals(bce.getBeanName())) {
throw bce;
}
else {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, ex.getMessage(), ex);
}
}最后,创建Bean和依赖注入完成后,会将Bean放入一级缓存
txtorg.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>)在以上方法中有如下代码:
javaif (newSingleton) { try { addSingleton(beanName, singletonObject); } catch (IllegalStateException ex) { Object object = this.singletonObjects.get(beanName); if (singletonObject != object) { throw ex; } } }
3. 为什么需要第三级缓存
三级缓存的真正作用:处理 AOP 代理。两级缓存的问题在于,它无法优雅地处理 AOP(面向切面编程)代理。
想象一下 Bean A 和 B 相互依赖,并且 A 需要被 AOP 代理(例如,A 上有 @Transactional 注解)。
首先创建 A,Spring 实例化 A;
现在,如果 Spring 直接将 A 的原始实例放入
earlySingletonObjects;然后对A对象进行依赖注入,发现需要B对象,进行B对象的创建,发现 B 依赖 A,B 获取并注入的是 A 的原始实例;
当 A 完成初始化并准备被放入
singletonObjects时,Spring 才会对 A 进行 AOP 代理;AOP代理是在组件初始化后才进行的,请参考 AOP
问题来了:此时
B中注入的是 A 的原始实例,而不是 A 的代理实例。这会导致B调用A的方法时,A上的 AOP 功能(如事务)不会生效,这是不正确的。
为了确保注入给其他 Bean 的始终是最终的 Bean 实例(可能是原始实例,也可能是代理实例),singletonFactories 应运而生。
为什么有了三级缓存,就能保证注入其他Bean的就是最终Bean实例呢?答案就在于向第三级缓存中加入的ObjectFactory是什么:
java
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}getEarlyBeanReference() 代码如下:
java
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;
}bp.getEarlyBeanReference() 如下:
txt
org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReferencejava
default Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
return bean;
}org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator是其实现类:

这就是AOP的自动代理类呀!
方法如下:
java
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
this.earlyBeanReferences.put(cacheKey, bean);
return this.wrapIfNecessary(bean, beanName, cacheKey);
}看到了熟悉的wrapIfNecessary()方法,这个方法会返回代理对象。
所以,有了三级缓存,当B对象从三级缓存中找到A对象的ObjectFactory后,调用getObject()方法,会调用wrapIfNecessary()方法,最后会返回代理对象。
4. 能不能去掉第二级缓存
前面分析了第三级缓存的必要性,那能不能去掉第二级缓存呢?
如果没有 earlySingletonObjects 会发生什么?假设我们只有 singletonObjects 和 singletonFactories。
考虑 Bean A 和 Bean B 循环依赖,并且 Bean A 需要被 AOP 代理。
实例化 A:Spring 实例化 Bean A。
A 放入
singletonFactories:Spring 创建 Bean A 的ObjectFactory并将其放入singletonFactories。A 填充属性,依赖 B:Bean A 开始填充属性,发现依赖 Bean B,于是去创建 Bean B。
创建 B,依赖 A:Bean B 开始创建。当 Bean B 发现依赖 Bean A 时:
它会检查
singletonObjects(没有 Bean A)。它会去
singletonFactories查找 Bean A 的ObjectFactory。找到了!Spring 调用 Bean A 的
ObjectFactory来获取 Bean A 的早期引用。在这个过程中,如果 Bean A 需要 AOP 代理,代理对象(A')会被创建。问题来了:如果没有
earlySingletonObjects,这个A'应该放在哪里呢?如果直接放入
singletonObjects,那么 Bean A 就被标记为“完成”了,但它实际上还没完成属性填充和初始化方法。如果不存,那么下次另一个 Bean C 也依赖 Bean A,它会再次从
singletonFactories获取,并再次创建代理对象,这显然是浪费的。
所以earlySingletonObjects 的作用是避免重复创建代理和性能优化,earlySingletonObjects 作为一个临时性的中转站和缓存,用来存放那些已经通过 ObjectFactory 生成的早期 Bean 引用(可能是原始实例,也可能是代理实例)。
5. 循环依赖的局限性
Spring只能处理set依赖注入的循环依赖,无法处理构造方法注入的循环依赖。
例如,下面这种情况会报错:
java
@Component
public class ServiceX {
private final ServiceY serviceY;
@Autowired
public ServiceX(ServiceY serviceY) {
this.serviceY = serviceY;
}
}
@Component
public class ServiceY {
private final ServiceX serviceX;
@Autowired
public ServiceY(ServiceX serviceX) {
this.serviceX = serviceX;
}
}如果要解决构造方法注入的循环依赖,可以使用@Lazy注解:
java
@Component
public class ServiceX {
private final ServiceY serviceY;
@Autowired
public ServiceX(@Lazy ServiceY serviceY) {
this.serviceY = serviceY;
}
}
@Component
public class ServiceY {
private final ServiceX serviceX;
@Autowired
public ServiceY(ServiceX serviceX) {
this.serviceX = serviceX;
}
}