肝!!!瞎扯,Spring只能解决setter注入方式的循环依赖?不存在的!

别的先不扯,先上结论

注:本文提及的循环依赖指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会按照正常的逻辑走一遍流程:

  1. 会将B打标记,标记B是一个正在创建的对象,并且添加到singletonsCurrentlyInCreation集合中,此时会把B放入singletonsCurrentlyInCreation集合中。【重点划一下】ps:此时singletonsCurrentlyInCreation集中中已经有了A,B
  2. 然后调用doCreateBean,使用默认无参构造器创建BeanWrapper对象。
  3. 判断是否需要放入singletonFactories缓存中,判断条件为【单例】【允许程序解决循环依赖】【当前bean存在singletonsCurrentlyInCreation集合中—即第一步打标时候放入该集合中】。
  4. 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

  1. 创建A,先从缓存里找,有没有A【第一次创建,连根毛都没】
  2. 之后将A进行标记,然后调用无参构造器返回A的BeanWrapper。
  3. 然后A将自己暴露在singletonFactories缓存中。
  4. 接着调用populateBean()方法进行属性填充。
  5. 这时候会发现存在属性B,接着调用getBean(B)创建B。
  6. 先从缓存里找B,这里也是啥也没有。
  7. 然后将B进行标记,调用有参构造器创建A【此时并没有将B暴露在singletonFactories中】
  8. 这时候创建A,从缓存里找,找到了singletonFactories中的A,调用getObject()方法返回A对象。
  9. 这时B就能返回B的BeanWrapper对象。
  10. 然后B进行属性填充populateBean(),初始化initializeBean()。
  11. 最后A完成自己的initializeBean()。

Q:构造器注入如何解决循环依赖?

参考文档:
Spring构造器注入循环依赖的解决方案及原理探索

使用@Lazy注解,对于强依赖的对象,一开始并不注入对象本身,而是注入其代理对象,以便顺利完成实例的构造,形成一个完整的对象,这样与其它应用层对象就不会形成互相依赖的关系;当需要调用真实对象的方法时,通过TargetSource去拿到真实的对象[DefaultListableBeanFactory#doResolveDependency],然后通过反射完成调用 参考文档:


版权声明:本文为qq_31426247原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。