什么是循环依赖?我们都知道Spring最大的作用就是来替我们管理Bean的,当然也包括Bean的创建以及整个生命周期,但是有这么一种情况,假设有三个类A、B、C需要交给Spring来管理,但A实例的创建需要先有B实例,而B实例的创建需要先有C实例,C实例的创建需要先有A实例,这样三个类就自然形成了一个环状结构,如果用代码来表示,如下:
public class TestA { TestB testB; get; set; } public class TestB { TestC testC; get; set; } public class TestC { TestA testA; get; set; }
这样,三个类就彼此形成了一个环状,那么Spring是如何来处理这样的状况呢?
有三种情况:
先说结论基于构造器的循环依赖Spring是无法解决的,是因为没有加入提前曝光的集合中,加入集合的条件是已经创建了Bean的包装对象,而构造注入的时候,并没有完成对象的创建,下面会有代码说明。
测试用例:
xml文件:
<bean id="testA" class="com.nmys.story.springCore.loop_dependency.loop01.LoopA"> <constructor-arg index="0" ref="testB"/> </bean> <bean id="testB" class="com.nmys.story.springCore.loop_dependency.loop01.LoopB"> <constructor-arg index="0" ref="testC"/> </bean> <bean id="testC" class="com.nmys.story.springCore.loop_dependency.loop01.LoopC"> <constructor-arg index="0" ref="testA"/> </bean>
测试类:
/** * description:测试通过有参构造方式注入产生的循环依赖问题 * @author 70KG * @date 2018/12/21 */ public class Test02 { @Test public void m1() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("test02.xml"); } }
分析上面代码:
其中"当前创建Bean池"就是一个Set集合,DefaultSingletonBeanRegistry类中beforeSingletonCreation方法,代码如下:
protected void beforeSingletonCreation(String beanName) { if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } }
然后我们来到创建Bean实例的地方:
AbstractAutowireCapableBeanFactory类的543行,通过这个方法返回一个这个Bean的包装对象:
--> instanceWrapper = createBeanInstance(beanName, mbd, args);----> 进入这个方法
--> AbstractAutowireCapableBeanFactory类的1129行
// Need to determine the constructor... // 需要确定构造函数,也就是说构造方法的循环依赖会在这儿return Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); if (ctors != null || mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { return autowireConstructor(beanName, mbd, ctors, args); } // No special handling: simply use no-arg constructor. // 无需特殊处理,仅使用无参构造即可,setter的循环依赖会在这个地方return return instantiateBean(beanName, mbd);
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
首先说结论:Spring是可以为我们解决这样的依赖的,原理说白了就是用了缓存处理,也就是常说的提前曝光,为什么叫提前曝光呢?因为这个缓存中的Bean是一个还未进行赋值的Bean,仅仅是一个引用而已。
xml文件:
<bean id="testA" class="com.nmys.story.springCore.loop_dependency.loop01.LoopA"> <property name="loopB" ref="testB"/> </bean> <bean id="testB" class="com.nmys.story.springCore.loop_dependency.loop01.LoopB"> <property name="loopC" ref="testC"/> </bean> <bean id="testC" class="com.nmys.story.springCore.loop_dependency.loop01.LoopC"> <property name="loopA" ref="testA"/> </bean>
测试类:
/** * description:通过setter注入产生的循环依赖问题 * @author 70KG */ public class Test03 { @Test public void m1() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("test03.xml"); } }
代码分析:
// 正在创建testA对象 Creating shared instance of singleton bean 'testA' Creating instance of bean 'testA' // 在缓存早期引用,目的是防止循环引用问题 Eagerly caching bean 'testA' to allow for resolving potential circular references Creating shared instance of singleton bean 'testB' Creating instance of bean 'testB' Eagerly caching bean 'testB' to allow for resolving potential circular references Creating shared instance of singleton bean 'testC' Creating instance of bean 'testC' Eagerly caching bean 'testC' to allow for resolving potential circular references // 在创建testC的时候会去缓存中拿原来存储的testA,并返回,但此时的testA是一个不完全的对象,也就是尚未初始化 Returning eagerly cached instance of singleton bean 'testA' that is not fully initialized yet - a consequence of a circular reference // 紧接着完成C的创建,顺便其它的也完成了 Finished creating instance of bean 'testC' Finished creating instance of bean 'testB' Finished creating instance of bean 'testA' Returning cached instance of singleton bean 'testB' Returning cached instance of singleton bean 'testC'
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
首先说结论,对于多例情况下的循环依赖,是无法解决的,因为Spring容器不进行缓存,更无法提前暴露。
测试用例:
xml文件:
<bean id="testA" class="com.nmys.story.springCore.loop_dependency.loop01.LoopA" scope="prototype"> <property name="loopB" ref="testB"/> </bean> <bean id="testB" class="com.nmys.story.springCore.loop_dependency.loop01.LoopB" scope="prototype"> <property name="loopC" ref="testC"/> </bean> <bean id="testC" class="com.nmys.story.springCore.loop_dependency.loop01.LoopC" scope="prototype"> <property name="loopA" ref="testA"/> </bean>
测试类:
/** * description:通过setter注入产生的循环依赖问题 * @author 70KG */ public class Test03 { @Test public void m1() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("test03.xml"); LoopA loopA = context.getBean(LoopA.class); System.out.println(loopA); } }
Object sharedInstance = getSingleton(beanName);
到此,全文完,自我感觉比其他的整理还算详细,如有疑问,请留言。