循环依赖即两个及以上的bean对象互相持有对方的引用,最终造成一个闭环。web
Spring容器会将每个正在建立的Bean 标识符放在一个“当前建立Bean池”中,Bean标识符在建立过程当中将一直保持
在这个池中,所以若是在建立Bean过程当中发现本身已经在“当前建立Bean池”里时将抛出
BeanCurrentlyInCreationException异常表示循环依赖;而对于建立完毕的Bean将从“当前建立Bean池”中清除掉。spring
public class BeanA { private BeanB b; public BeanA(BeanB b) { this.b = b; } } public class BeanB { private BeanC c; public BeanB(BeanC c) { this.c = c; } } public class BeanC { private BeanA a; public BeanC(BeanA a) { this.a = a; } } ---------------------- <bean id="a" class="com.raycloud.dmj.data.utils.BeanA"> <constructor-arg index="0" ref="b"/> </bean> <bean id="b" class="com.raycloud.dmj.data.utils.BeanB"> <constructor-arg index="0" ref="c"/> </bean> <bean id="c" class="com.raycloud.dmj.data.utils.BeanC"> <constructor-arg index="0" ref="a"/> </bean> ---------------------- ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
报错:
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name ‘a’: Requested bean is currently in creation: Is there an unresolvable circular reference?缓存
Spring容器先建立单例BeanA,BeanA依赖BeanB,而后将A放在“当前建立Bean池”中,此时建立BeanB,BeanB依赖BeanC ,而后将B放在“当前建立Bean池”中,此时建立BeanC,StudentC又依赖BeanA, 可是,此时BeanA已经在池中,因此会报错,,由于在池中的Bean都是未初始化完的,因此会依赖错误 ,(初始化完的Bean会从池中移除)svg
public class BeanA { public BeanB b; public void setB(BeanB b) { this.b = b; } } public class BeanB { public BeanC c; public void setC(BeanC c) { this.c = c; } } public class BeanC { public BeanA a; public void setA(BeanA a) { this.a = a; } } ---------------------- <bean id="a" class="com.raycloud.dmj.data.utils.BeanA"> <property name="b" ref="b"/> </bean> <bean id="b" class="com.raycloud.dmj.data.utils.BeanB"> <property name="c" ref="c"/> </bean> <bean id="c" class="com.raycloud.dmj.data.utils.BeanC"> <property name="a" ref="a"/> </bean> ---------------------- ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); BeanA a = context.getBean("a", BeanA.class); BeanB b = context.getBean("b", BeanB.class); BeanC c = context.getBean("c", BeanC.class); System.out.println(String.format("a:%s,a.b:%s",a,a.b)); System.out.println(String.format("b:%s,b.c:%s",b,b.c)); System.out.println(String.format("c:%s,c.a:%s",c,c.a));
输出:
a:com.raycloud.dmj.data.utils.BeanA@46bfc63c,a.b:com.raycloud.dmj.data.utils.BeanB@586fb16d
b:com.raycloud.dmj.data.utils.BeanB@586fb16d,b.c:com.raycloud.dmj.data.utils.BeanC@ce99877
c:com.raycloud.dmj.data.utils.BeanC@ce99877,c.a:com.raycloud.dmj.data.utils.BeanA@46bfc63c
能够看到单例setter循环依赖没有报错,且循环的依赖都成功set。具体实现原理后面详细看this
<bean id="a" class="com.raycloud.dmj.data.utils.BeanA" scope="prototype"> <property name="b" ref="b"/> </bean> <bean id="b" class="com.raycloud.dmj.data.utils.BeanB" scope="prototype"> <property name="c" ref="c"/> </bean> <bean id="c" class="com.raycloud.dmj.data.utils.BeanC" scope="prototype"> <property name="a" ref="a"/> </bean>
一样报错:
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name ‘a’: Requested bean is currently in creation: Is there an unresolvable circular reference?prototype
所以能够知道多例模式下也不能解决循环依赖。
为何?
对于“prototype”做用域Bean,Spring容器没法完成依赖注入,由于“prototype”做用域的Bean,Spring容
器不进行缓存,所以没法提早暴露一个建立中的Bean。code
如图,set方法注入,spring先实例化Bean[经过无参构造器],在设置属性,这样就不会报错了。
具体如何解决循环依赖
以咱们的实例来讲,spring会先实例化a,b,c,并放入一个map,而后设置属性,a设置属性b,只须要从mao中取出b便可,以此类推。orm
而事实上spring可不止这一个map,而是经过三级缓存来解决单例Bean的循环依赖。xml
/** Cache of singleton objects: bean name --> bean instance */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256); /** Cache of singleton factories: bean name --> ObjectFactory */ private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16); /** Cache of early singleton objects: bean name --> bean instance */ private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
singletonObjects:存放单例对象实例的缓存
earlySingletonObjects:存在提早曝光的bean,也就是正在建立中的bean。
singletonFactories :建立单例对象的工厂对象
实现原理的代码以下:
protected Object getSingleton(String beanName, boolean allowEarlyReference) { //先从单例缓存中取 Object singletonObject = this.singletonObjects.get(beanName); //一级缓存中没有,而且判断正在建立中【好比A的构造器依赖B,或者已经实例化正在setB ,因此先去建立B,那么A就是建立中】 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; }
简单来讲: 在建立单例Bean的时候是这样解决循环依赖的。 假设A B互相依赖。 先经过createBean建立A,会先走createInstance来实例化A,而后把A的单例工厂放到三级缓存,实例化后须要设置属性,发现须要B,可是B没有初始化,所以经过createBean建立,一样须要实例化,实例化之后发现依赖A,所以先去单例缓存中找,由于A还在建立中,因此找不到,而后去二级缓存找,依旧找不到,所以最后经过单例工厂建立获取了A,B就建立好了,B建立好了,就set给A,此时A.B都实例化成功了。