别的先不扯,先上结论
注:本文提及的循环依赖指Bean的作用域为默认的Singleton,而非Prototype
| 方式 | 依赖情况 | 注入方式 | 能够解决循环依赖 |
|---|---|---|---|
| 情况一 | AB相互依赖 | 均采用setter方式 | 能 |
| 情况二 | AB相互依赖 | 均采用构造器方式 | 不能 |
| 情况三 | AB相互依赖 | A中注入B采用setter,B中注入A采用构造器 | 能 |
| 情况四 | AB相互依赖 | A中注入B采用构造器,B中注入A采用setter | 不能 |
测试数据
情况四:
<bean id="a" class="com.mashibing.cycle.A">
<constructor-arg ref="b"/>
</bean>
<bean id="b" class="com.mashibing.cycle.B">
<property name="a" ref="a"></property>
</bean>
public class A {
private B b;
public A(B b) {
this.b = b;
}
}
public class B {
private A a;
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
}
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("cycle.xml");
}
Spring在创建Bean时默认会根据自然排序进行创建,所以A会先于B进行创建。
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>)

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#beforeSingletonCreation
beforeSingletonCreation()方法会把当前正在创建的bean坐标记,并且添加到singletonsCurrentlyInCreation集合中,此时会把A放入singletonsCurrentlyInCreation集合中。【重点划一下】
由于A存在有参的构造方法,所以在创建实例的时候,不会调用默认的无参构造器。这里会调用A的有参构造方法
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance ###创建实例
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#autowireConstructor ###有参构造方法
在进行有参构造方法创建实例的时候会去解析方法的参数,这里即B。
org.springframework.beans.factory.support.ConstructorResolver#resolveConstructorArguments ###解析构造方法的参数
在解析构造函数参数的时候,会去获取参数对象的实例,即B的实例,
org.springframework.beans.factory.support.BeanDefinitionValueResolver#resolveValueIfNecessary
org.springframework.beans.factory.support.BeanDefinitionValueResolver#resolveReference
###解析出对应ref所封装的Bean元信息(即Bean名,Bean类型)的Bean对象,在工厂中解决对另一个bean的引用

这里会继续去获取B的实例。
这时候B会按照正常的逻辑走一遍流程:
- 会将B打标记,标记B是一个正在创建的对象,并且添加到singletonsCurrentlyInCreation集合中,此时会把B放入singletonsCurrentlyInCreation集合中。【重点划一下】ps:此时singletonsCurrentlyInCreation集中中已经有了A,B
- 然后调用doCreateBean,使用默认无参构造器创建BeanWrapper对象。
- 判断是否需要放入singletonFactories缓存中,判断条件为【单例】【允许程序解决循环依赖】【当前bean存在singletonsCurrentlyInCreation集合中—即第一步打标时候放入该集合中】。
- populateBean()方法调用,进行属性填充。
这个时候B对象中存在A属性,这时候又会去调用beanFactory.getBean(A)的方法。即走一遍创建bean的流程。
getBean()--->doGetBean()--->createBean()--->doCreateBean()
关键点来了!!!喝瓶红牛提提神!
按照正常逻辑会从org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)方法中获取A曝光在singletonFactories缓存中的objectFactory。
但是此时还处于A对象获取BeanWrapper对象的过程中,也就是说A对象还没有执行往singletonFactories缓存放objectFactory的操作,所以此时org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String)返回的还是null。
然后继续执行org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>)【注意与上面getSingleton的区别】

还是熟悉的配方,还是熟悉的味道,这边依然进行对象的标记,这时候是标记A
这时候singletonsCurrentlyInCreation集合中已经有A了,所以抛出了BeanCurrentlyInCreationException()。
根本原因也就是在A调用有参构造函数的时候,自己本身打了标记但是还没放入singletonFactories缓存中,就去创建B,这时候B放入缓存,也加了标记,在填充A属性的时候,由于没有提前暴露A,所以又去打标,从而导致一个A被两次标记,所以抛出了异常。如果A提前在缓存中暴露了,就会直接调用getObject返回实例,不会再去创建。这也就是情况三为什么不会出错的原因。
最后还是来捋捋情况三【A中注入B采用setter,B中注入A采用构造器】
老规矩,还是先创建A
- 创建A,先从缓存里找,有没有A【第一次创建,连根毛都没】
- 之后将A进行标记,然后调用无参构造器返回A的BeanWrapper。
- 然后A将自己暴露在singletonFactories缓存中。
- 接着调用populateBean()方法进行属性填充。
- 这时候会发现存在属性B,接着调用getBean(B)创建B。
- 先从缓存里找B,这里也是啥也没有。
- 然后将B进行标记,调用有参构造器创建A【此时并没有将B暴露在singletonFactories中】
- 这时候创建A,从缓存里找,找到了singletonFactories中的A,调用getObject()方法返回A对象。
- 这时B就能返回B的BeanWrapper对象。
- 然后B进行属性填充populateBean(),初始化initializeBean()。
- 最后A完成自己的initializeBean()。
Q:构造器注入如何解决循环依赖?
参考文档:
Spring构造器注入循环依赖的解决方案及原理探索
使用@Lazy注解,对于强依赖的对象,一开始并不注入对象本身,而是注入其代理对象,以便顺利完成实例的构造,形成一个完整的对象,这样与其它应用层对象就不会形成互相依赖的关系;当需要调用真实对象的方法时,通过TargetSource去拿到真实的对象[DefaultListableBeanFactory#doResolveDependency],然后通过反射完成调用 参考文档: