循环依赖:是一个或多个对象实例之间存在直接或间接的依赖关系,这种依赖关系构成了构成一个环形调用



前面两种情况的直接循环依赖比较直观,非常好识别,但是第三种间接循环依赖的情况有时候因为业务代码调用层级很深,不容易识别出来
循环依赖场景

单例的setter注入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @Service public class TestService1 {
@Autowired private TestService2 testService2;
public void test1() { } }
@Service public class TestService2 {
@Autowired private TestService1 testService1;
public void test2() { } }
|
这是一个经典的循环依赖,但是它能正常运行,得益于spring的内部机制,让我们根本无法感知它有问题,因为spring默默帮我们解决了,将实例化与初始化步骤分开,在中间过程中给其他对象赋值的时候,并不是一个完整对象,而是把半成品对象赋值给了其他对象
Bean创建前有一个集合singletonsCurrentlyInCreation,用于标记正在创建这个状态
一级缓存:为“Spring 的单例属性”而生,就是个单例池,用来存放已经初始化完成的单例 Bean
二级缓存:为“解决 AOP”而生,存放的是半成品的 AOP 的单例 Bean
三级缓存:为“打破循环”而生,存放的是生成半成品单例 Bean 的工厂方法,SingletonFactories是个函数式接口,lambda表达式
spring内部有三级缓存:
singletonObjects
一级缓存,用于保存实例化、注入、初始化完成的bean实例
earlySingletonObjects
二级缓存,用于保存实例化完成的bean实例
singletonFactories
三级缓存,用于保存bean创建工厂,以便于后面扩展有机会创建代理对象。
实例化bean之前先标记为创建状态

- 先实例化
testService1
,doCreateBean
,发现是创建状态,三级缓存存入lambda表达式,这个表达式是生成bean的代理对象的,如果是AOP则反射对象,否则bean自身,是个半成品,添加A的代理对象到3级缓存
- 走到
populateBean
方法中,填充属性通过AutowiredAnnotationBeanPostProcess
的postProcessProperties()
方法发现依赖testService2
,继续递归testService2
,重复1步骤
- 发现
testService2
依赖 testService1
,再解决testService2
的依赖
- 发现
testService1
现在在3级缓存已经有了,把testService1
从3级缓存删除,存到2级缓存,此时三级缓存存testService2
,二级缓存存testService1
- 继续返回第二层,
testService2
填充了testService1
的属性,从创建状态删除,把testService2
添加到一级缓存,删除二级缓存
- 再返回第一层,
testService1
填充了testService2
的属性,从创建状态删除,把testService1
添加到一级缓存,删除二级缓存
源码分析
DefaultSingletonBeanRegistry#getSingleton(String)
获取单例 Bean 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public Object getSingleton(String beanName) { return getSingleton(beanName, true); } @Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject; }
|
上面的代码就是 Spring 尝试从缓存中加载单例。单例在 Spring 的同一个容器中只会被创建一次,后续再获取 bean,就直接从缓存中取了
补充一些方法和参数
isSingletonCurrentlyInCreation()
:判断当前单例bean是否正在建立中,也就是没有初始化完成(好比A的构造器依赖了B对象因此得先去建立B对象, 或则在A的populateBean过程当中依赖了B对象,得先去建立B对象,这时的A就是处于建立中的状态。)
allowEarlyReference
:是否容许从singletonFactories中经过getObject拿到对象
先认识下 DefaultSingletonBeanRegistry
这个类里面的成员变量
Map<String, Object> singletonObjects
key 就是 beanName ,value 就是 bean 实例
Map<String, ObjectFactory<?>> singletonFactories
key 为 beanName,value 为创建 bean 的工厂
Map<String, Object> earlySingletonObjects
key 为 beanName ,value 为 bean。但是和 singletonObjects
不同的是,bean 被加入到 earlySingletonObjects
的时候、这个 bean 还是处于一种创建中的状态,目的也很简单、Spring 用来解决某些场景下的循环依赖
Spring获取一个bean的简单过程:
- 首先从
singletonObjects
获取,也就是单例IoC容器中
- 然后从
earlySingletonObjects
中获取,也就是通过ObjectFactory
实现的提前曝光的容器
- 最后从
singletonFactories
获取,也就是实例化bean的实例工厂
- 如果都获取不到,则新创建一个bean对象,也就说走上面分析的流程
从缓存中获取的bean的过程,一般都称为三级缓存,前面三个步骤对应了1,2,3级缓存。接下来举一个案例分析这个过程,假如有bean的依赖关系为:A->B->C->A,当然这些都是基于属性依赖的,当A执行到populateBean
方法实现属性注入的时候,会先去获取B实例,然后B执行populateBean
会获取C实例,C执行到populateBean
获取查找A实例,此时A实例正在被创建,又会循环上述过程,产生了循环依赖问题。Spring获取getBean()
最终调用下面简化后的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| protected <T> T doGetBean( final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException { final String beanName = transformedBeanName(name); Object bean; Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { try { if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() { @Override public Object getObject() throws BeansException { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { throw ex; } } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } .............. } return (T) bean; }
|
当A去查找bean的实例的时候,会调用上面的doGetBean
方法获取,这个方法里面有两个关注点,分别是两个重载方法getSingleton
,首先当A执行populateBean
查找B的实例时调用第一个重载:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| protected Object getSingleton(String beanName, boolean allowEarlyReference) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return (singletonObject != NULL_OBJECT ? singletonObject : null); }
|
这个方法是从三级缓存中查找bean,第一级缓存singletonObjects
里面放置的是实例化好的单例对象。第二级earlySingletonObjects
里面存放的是提前曝光的单例对象(没有完全装配好)。第三级singletonFactories
里面存放的是要被实例化的对象的对象工厂,由于B第一次获取还没有被创建,所以一级缓存singletonObjects
获取结果肯定为null,再看看看看进入二级缓存中的条件isSingletonCurrentlyInCreation(beanName)
:
1 2 3 4
| public boolean isSingletonCurrentlyInCreation(String beanName) { return this.singletonsCurrentlyInCreation.contains(beanName); }
|
上面的步骤中并没有任何操作往isSingletonCurrentlyInCreation
中加入B的beanName的操作,所以压根不会进入二级缓存,直接就返回null了,然后就判断bean是否时单例的,如果时调用getSingleton(String beanName, ObjectFactory objectFactory)
,此时objectFactory时一个匿名内部类,实例B的获取是通过内部类的createBean
获取的,这个是我们关注点2 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(beanName, "'beanName' must not be null"); synchronized (this.singletonObjects) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { if (this.singletonsCurrentlyInDestruction) { throw new BeanCreationNotAllowedException(beanName,"不能从销毁的bean中创建"); } beforeSingletonCreation(beanName); boolean recordSuppressedExceptions = (this.suppressedExceptions == null); if (recordSuppressedExceptions) { this.suppressedExceptions = new LinkedHashSet<Exception>(); } try { singletonObject = singletonFactory.getObject(); } catch (BeanCreationException ex) { if (recordSuppressedExceptions) { for (Exception suppressedException : this.suppressedExceptions) { ex.addRelatedCause(suppressedException); } } throw ex; } finally { if (recordSuppressedExceptions) { this.suppressedExceptions = null; } afterSingletonCreation(beanName); } addSingleton(beanName, singletonObject); } return (singletonObject != NULL_OBJECT ? singletonObject : null); } }
|
这个方法首先会从一级缓存中查找B,很明显,查到的结果为null,然后调用beforeSingletonCreation(beanName)
,将B的beanName添加到singletonsCurrentlyInCreation
中,也就是关注点1中无法进入二级缓存的那个集合校验:
1 2 3 4 5 6
| protected void beforeSingletonCreation(String beanName) { if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } }
|
紧接着就会调用singletonFactory.getObject()
创建名,也就是通过匿名内部类的createBean
方法创建,前面分析过,创建bean最终会调用doCreateBean
方法,这个方法简化了, 只看最核心的关注点3:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) { BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } ...........代码省略........... boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isDebugEnabled()) { logger.debug("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } addSingletonFactory(beanName, new ObjectFactory<Object>() { @Override public Object getObject() throws BeansException { return getEarlyBeanReference(beanName, mbd, bean); } }); } Object exposedObject = bean; try { populateBean(beanName, mbd, instanceWrapper); if (exposedObject != null) { exposedObject = initializeBean(beanName, exposedObject, mbd); } } catch (Throwable ex) { throw ex; } if (earlySingletonExposure) { Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { exposedObject = earlySingletonReference; } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length); for (String dependentBean : dependentBeans) { if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } } } } ...........代码省略........... return exposedObject; }
|
createBeanInstance
利用反射创建了对象,下面我们看看关注点3earlySingletonExposure
属性值的判断,其中有一个判断点就是isSingletonCurrentlyInCreation(beanName)
1 2 3
| public boolean isSingletonCurrentlyInCreation(String beanName) { return this.singletonsCurrentlyInCreation.contains(beanName); }
|
发现使用的是singletonsCurrentlyInCreation
这个集合,在上面的步骤中将的B的BeanName已经填充进去了,所以可以查到,而且在初始化bean的时候,还会判断检查bean是否有循环依赖,而且是否允许循环依赖,这里的ABC形成了循环依赖,所以最终earlySingletonExposure
结合其他的条件综合判断为true,进行下面的流程addSingletonFactory
,这里是为这个Bean添加ObjectFactory
,这个BeanName(A)
对应的对象工厂,他的getObject
方法的实现是通过getEarlyBeanReference
这个方法实现的。首先我们看下addSingletonFactory
的实现
1 2 3 4 5 6 7 8 9 10
| 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); } } }
|
往三级缓存singletonFactories
存放数据,清除二级缓存中beanName对应的数据。这里有个很重要的点,是往三级缓存里面存入了值,这是Spring处理循环依赖的核心点。getEarlyBeanReference
这个方法是getObject
的实现,可以简单认为是返回了一个为填充完毕的A的对象实例。设置完三级缓存后,就开始了填充A对象属性的过程
上面理清之后整体来分析以下ABC的初始化流程,当设置A的属性时,发现需要B类型的Bean,于是继续调用getBean
方法创建,这次的流程和上面A的完全一致,然后到了填充C类型的Bean的过程,同样的调用getBean(C)来执行,同样到了填充属性A的时候,调用了getBean(A),我们从这里继续说,调用了doGetBean中的`Object sharedInstance = getSingleton(beanName),还是关注点1的代码,但是处理逻辑完全不一样了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| protected Object getSingleton(String beanName, boolean allowEarlyReference) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return (singletonObject != NULL_OBJECT ? singletonObject : null); }
|
还是从singletonObjects获取对象获取不到,因为A是在singletonsCurrentlyInCreation
这个Set中,所以进入了下面的逻辑,从二级缓存earlySingletonObjects
中取,还是没有查到,然后从三级缓存singletonFactories
找到对应的对象工厂调用getObject
方法获取未完全填充完毕的A的实例对象,然后删除三级缓存的数据,填充二级缓存的数据,返回这个对象A。C依赖A的实例填充完毕了,虽然这个A是不完整的。不管怎么样C式填充完了,就可以将C放到一级缓存singletonObjects
同时清理二级和三级缓存的数据。同样的流程B依赖的C填充好了,B也就填充好了,同理A依赖的B填充好了,A也就填充好了。Spring就是通过这种方式来解决循环引用的

Spring不能解决构造器的循环依赖
构造器注入形成的循环依赖: 也就是beanB需要在beanA的构造函数中完成初始化,beanA也需要在beanB的构造函数中完成初始化,这种情况的结果就是两个bean都不能完成初始化,循环依赖难以解决
Spring解决循环依赖主要是依赖三级缓存,但是的在调用构造方法之前还未将其放入三级缓存之中,因此后续的依赖调用构造方法的时候并不能从三级缓存中获取到依赖的Bean,因此不能解决
Spring不能解决prototype作用域循环依赖
这种循环依赖同样无法解决,因为spring不会缓存‘prototype’作用域的bean,而spring中循环依赖的解决正是通过缓存来实现的
Spring不能解决多例的循环依赖
多实例Bean是每次调用一次getBean都会执行一次构造方法并且给属性赋值,根本没有三级缓存,因此不能解决循环依赖
那么实际开发中,类似的依赖是如何解决?
这类循环依赖问题解决方法很多,主要有:
- 使用@Lazy注解,延迟加载
- 使用@DependsOn注解,指定加载先后关系
- 修改文件名称,改变循环依赖类的加载顺序
这类循环依赖问题要找到@DependsOn注解循环依赖的地方,迫使它不循环依赖就可以解决问题
这类循环依赖问题可以通过把bean改成单例的解决
这类循环依赖问题可以通过使用@Lazy注解解决