看完这一篇文章如果你还不懂循环依赖,那只有葵花宝典可以救你了
上一篇文章剖析了下bean的加载流程,如果你认真看并且自己debug了一遍源码,那么我相信你应该对bean的加载流程有了大致的了解,但是因为篇幅问题,有一个重点没有写到,那就是spring容器的循环依赖,都说这玩意难,今天咱就看看它到底难在哪。
spring容器的循环依赖包括构造器循环依赖和setter循环依赖。
一、首先看下构造器的循环依赖,上代码:1.先定义三个实体类,这三个实体类有个特点,就是第TestA依赖TestB,TestB依赖TestC,TestC依赖TestA
package org.springframework.beans.factory.myspring.circle;/** * @Authror ayo * @Date 2020/11/21 20:10 */public class TestA { private TestB testB; public TestA(TestB testB) { this.testB = testB; }}
package org.springframework.beans.factory.myspring.circle;/** * @Authror ayo * @Date 2020/11/21 20:10 */public class TestB { private TestC testC; public TestB(TestC testC) { this.testC = testC; }}
package org.springframework.beans.factory.myspring.circle;/** * @Authror ayo * @Date 2020/11/21 20:11 */public class TestC { private TestA testA; public TestC(TestA testA) { this.testA = testA; }}
2.添加配置文件
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="testA" class="org.springframework.beans.factory.myspring.circle.TestA"> <constructor-arg index="0" ref="testB">constructor-arg> bean> <bean id="testB" class="org.springframework.beans.factory.myspring.circle.TestB"> <constructor-arg index="0" ref="testC">constructor-arg> bean> <bean id="testC" class="org.springframework.beans.factory.myspring.circle.TestC"> <constructor-arg index="0" ref="testA">constructor-arg> bean>beans>
3.写个测试类
package org.springframework.beans.factory.myspring;import org.junit.Test;import org.junit.rules.ExpectedException;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.BeanCurrentlyInCreationException;import org.springframework.beans.factory.BeanFactory;import org.springframework.beans.factory.myspring.circle.TestA;import org.springframework.beans.factory.xml.XmlBeanFactory;import org.springframework.core.io.ClassPathResource;/** * @Authror ayo * @Date 2020/11/16 17:52 */public class BeanFactoryTest { private static final Logger logger = LoggerFactory.getLogger(BeanFactoryTest.class); /** * 测试构造器的循环依赖 */ @Test public void testCircleByConstructor() { try { XmlBeanFactory xmlBeanFactory = new XmlBeanFactory(new ClassPathResource("org/springframework/beans/factory/myspring/beanFactoryTest.xml")); xmlBeanFactory.getBean("testA"); }catch (Exception e){ logger.error("testCircleByConstructor error==>" + e.getMessage(), e); } }}
首先运行下看看
21:13:55.057 [main] ERROR org.springframework.beans.factory.myspring.BeanFactoryTest - testCircleByConstructor error==>Error creating bean with name 'testA' defined in class path resource [org/springframework/beans/factory/myspring/beanFactoryTest.xml]: Cannot resolve reference to bean 'testB' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'testB' defined in class path resource [org/springframework/beans/factory/myspring/beanFactoryTest.xml]: Cannot resolve reference to bean 'testC' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'testC' defined in class path resource [org/springframework/beans/factory/myspring/beanFactoryTest.xml]: Cannot resolve reference to bean 'testA' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'testA': Requested bean is currently in creation: Is there an unresolvable circular reference?org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'testA' defined in class path resource [org/springframework/beans/factory/myspring/beanFactoryTest.xml]: Cannot resolve reference to bean 'testB' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'testB' defined in class path resource [org/springframework/beans/factory/myspring/beanFactoryTest.xml]: Cannot resolve reference to bean 'testC' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'testC' defined in class path resource [org/springframework/beans/factory/myspring/beanFactoryTest.xml]: Cannot resolve reference to bean 'testA' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'testA': Requested bean is currently in creation: Is there an unresolvable circular reference? at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:314) ~[classes/:?] at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:110) ~[classes/:?] at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:626) ~[classes/:?] at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:154) ~[classes/:?] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1266) ~[classes/:?] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1123) ~[classes/:?] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:535) ~[classes/:?] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:495) ~[classes/:?] at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:319) ~[classes/:?] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:235) ~[classes/:?] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:317) ~[classes/:?] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:201) ~[classes/:?] at org.springframework.beans.factory.myspring.BeanFactoryTest.testCircleByConstructor(BeanFactoryTest.java:39) [classes/:?] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_181] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_181] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_181] at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_181] at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) [junit-4.12.jar:4.12] at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) [junit-4.12.jar:4.12] at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) [junit-4.12.jar:4.12] at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) [junit-4.12.jar:4.12] at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) [junit-4.12.jar:4.12] at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) [junit-4.12.jar:4.12] at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) [junit-4.12.jar:4.12] at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) [junit-4.12.jar:4.12] at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) [junit-4.12.jar:4.12] at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) [junit-4.12.jar:4.12] at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) [junit-4.12.jar:4.12] at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) [junit-4.12.jar:4.12] at org.junit.runners.ParentRunner.run(ParentRunner.java:363) [junit-4.12.jar:4.12] at org.junit.runner.JUnitCore.run(JUnitCore.java:137) [junit-4.12.jar:4.12] at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) [junit-rt.jar:?] at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47) [junit-rt.jar:?] at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) [junit-rt.jar:?] at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70) [junit-rt.jar:?]Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'testB' defined in class path resource [org/springframework/beans/factory/myspring/beanFactoryTest.xml]: Cannot resolve reference to bean 'testC' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'testC' defined in class path resource [org/springframework/beans/factory/myspring/beanFactoryTest.xml]: Cannot resolve reference to bean 'testA' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'testA': Requested bean is currently in creation: Is there an unresolvable circular reference? at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:314) ~[classes/:?] at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:110) ~[classes/:?] at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:626) ~[classes/:?] at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:154) ~[classes/:?] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1266) ~[classes/:?] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1123) ~[classes/:?] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:535) ~[classes/:?] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:495) ~[classes/:?] at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:319) ~[classes/:?] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:235) ~[classes/:?] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:317) ~[classes/:?] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:201) ~[classes/:?] at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:303) ~[classes/:?] ... 34 moreCaused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'testC' defined in class path resource [org/springframework/beans/factory/myspring/beanFactoryTest.xml]: Cannot resolve reference to bean 'testA' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'testA': Requested bean is currently in creation: Is there an unresolvable circular reference? at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:314) ~[classes/:?] at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:110) ~[classes/:?] at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:626) ~[classes/:?] at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:154) ~[classes/:?] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1266) ~[classes/:?] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1123) ~[classes/:?] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:535) ~[classes/:?] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:495) ~[classes/:?] at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:319) ~[classes/:?] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:235) ~[classes/:?] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:317) ~[classes/:?] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:201) ~[classes/:?] at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:303) ~[classes/:?] at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:110) ~[classes/:?] at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:626) ~[classes/:?] at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:154) ~[classes/:?] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1266) ~[classes/:?] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1123) ~[classes/:?] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:535) ~[classes/:?] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:495) ~[classes/:?] at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:319) ~[classes/:?] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:235) ~[classes/:?] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:317) ~[classes/:?] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:201) ~[classes/:?] at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:303) ~[classes/:?] ... 34 moreCaused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'testA': Requested bean is currently in creation: Is there an unresolvable circular reference? at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:352) ~[classes/:?] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228) ~[classes/:?] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:317) ~[classes/:?] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:201) ~[classes/:?] at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:303) ~[classes/:?] at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:110) ~[classes/:?] at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:626) ~[classes/:?] at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:154) ~[classes/:?] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1266) ~[classes/:?] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1123) ~[classes/:?] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:535) ~[classes/:?] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:495) ~[classes/:?] at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:319) ~[classes/:?] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:235) ~[classes/:?] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:317) ~[classes/:?] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:201) ~[classes/:?] at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:303) ~[classes/:?] at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:110) ~[classes/:?] at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:626) ~[classes/:?] at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:154) ~[classes/:?] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1266) ~[classes/:?] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1123) ~[classes/:?] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:535) ~[classes/:?] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:495) ~[classes/:?] at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:319) ~[classes/:?] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:235) ~[classes/:?] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:317) ~[classes/:?] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:201) ~[classes/:?] at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:303) ~[classes/:?] ... 34 more
看异常信息要看Caused by,从上到下代表越来越底层,所以就看最后一个Caused by报的什么:
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'testA': Requested bean is currently in creation: Is there an unresolvable circular reference? at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:352) ~[classes/:?]
不了解的小伙伴可以涨点姿势,如果你某一天启动项目的时候发现报了一个这种异常,就说明你的spring容器中发生了循环依赖导致启动失败,为什么?这里面流程有点长,如果我把图放出来不知道要多少张,估计图没放完你们都蒙圈了,所以我整了个视频:视频中的运行流程如下:
public Object getSingleton(String beanName, ObjectFactory> singletonFactory) { Assert.notNull(beanName, "Bean name must not be null"); synchronized (this.singletonObjects) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { if (this.singletonsCurrentlyInDestruction) { throw new BeanCreationNotAllowedException(beanName, "Singleton bean creation not allowed while singletons of this factory are in destruction " + "(Do not request a bean from a BeanFactory in a destroy method implementation!)"); } if (logger.isDebugEnabled()) { logger.debug("Creating shared instance of singleton bean '" + beanName + "'"); } //关键就在这个方法 /**protected void beforeSingletonCreation(String beanName) { if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); }}*/ beforeSingletonCreation(beanName); boolean newSingleton = false; boolean recordSuppressedExceptions = (this.suppressedExceptions == null); if (recordSuppressedExceptions) { this.suppressedExceptions = new LinkedHashSet<>(); } try { singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { // Has the singleton object implicitly appeared in the meantime -> // if yes, proceed with it since the exception indicates that state. singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { throw ex; } } catch (BeanCreationException ex) { if (recordSuppressedExceptions) { for (Exception suppressedException : this.suppressedExceptions) { ex.addRelatedCause(suppressedException); } } throw ex; } finally { if (recordSuppressedExceptions) { this.suppressedExceptions = null; } afterSingletonCreation(beanName); } if (newSingleton) { addSingleton(beanName, singletonObject); } } return singletonObject; } }
@Nullable private Object resolveReference(Object argName, RuntimeBeanReference ref) { try { Object bean; //拿到当前bean依赖的beanName String refName = ref.getBeanName(); refName = String.valueOf(doEvaluate(refName)); if (ref.isToParent()) { if (this.beanFactory.getParentBeanFactory() == null) { throw new BeanCreationException( this.beanDefinition.getResourceDescription(), this.beanName, "Can't resolve reference to bean '" + refName + "' in parent factory: no parent factory available"); } bean = this.beanFactory.getParentBeanFactory().getBean(refName); } else { //重新调用getBean来创建依赖的bean bean = this.beanFactory.getBean(refName); this.beanFactory.registerDependentBean(refName, this.beanName); } if (bean instanceof NullBean) { bean = null; } return bean; } catch (BeansException ex) { throw new BeanCreationException( this.beanDefinition.getResourceDescription(), this.beanName, "Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex); } }
1.首次调用AbstractBeanFactory#getBean,运行到!DefaultSingltonBeanRegistry.singletonsCurrentlyInCreation.add(beanName)时,这时候beanName是testA,将testA添加进去。
2.然后运行到BeanDefinitionValueResolver#resolveReference时候,发现testA中依赖testB,看第6行代码,拿到的ref是testB,然后第19行回去创建testB。
3.第二次调用AbstractBeanFactory#getBean,运行到!DefaultSingltonBeanRegistry.singletonsCurrentlyInCreation.add(beanName)时,这时候beanName是testB,将testB添加进去。
4.然后运行到BeanDefinitionValueResolver#resolveReference时候,发现testB中依赖testC,看第6行代码,拿到的ref是testC,然后第19行回去创建testC。
5.第三次调用AbstractBeanFactory#getBean,运行到!DefaultSingltonBeanRegistry.singletonsCurrentlyInCreation.add(beanName)时,这时候beanName是testC,将testC添加进去。
6.然后运行到BeanDefinitionValueResolver#resolveReference时候,发现testC中依赖testA,看第6行代码,拿到的ref是testA,然后第19行回去创建testA。
7.第四次调用AbstractBeanFactory#getBean,运行到!DefaultSingltonBeanRegistry.singletonsCurrentlyInCreation.add(beanName)时,这时候beanName是testA,将testA添加进去,但是!!这时候你会发现添加失败了,为什么?因为testA在第一次走getBean的时候已经被添加进去了,然后程序就抛异常了,循环依赖出现了!!!
通过上面的分析可以得出结论,通过构造器注入的循环依赖,spring容器是无法解决的,只能抛出BeanCurrentlyInCreationException的异常。
二、setter循环依赖,
上代码:
首先三个实体类需要做下修改,原来使用构造器注入,现在使用setter注入:
package org.springframework.beans.factory.myspring.circle;/** * @Authror ayo * @Date 2020/11/21 20:10 */public class TestA { private TestB testB; public TestB getTestB() { return testB; } public void setTestB(TestB testB) { this.testB = testB; }}
package org.springframework.beans.factory.myspring.circle;/** * @Authror ayo * @Date 2020/11/21 20:10 */public class TestB { private TestC testC; public TestC getTestC() { return testC; } public void setTestC(TestC testC) { this.testC = testC; }}
package org.springframework.beans.factory.myspring.circle;/** * @Authror ayo * @Date 2020/11/21 20:11 */public class TestC { private TestA testA; public TestA getTestA() { return testA; } public void setTestA(TestA testA) { this.testA = testA; }}
然后配置文件也做下改动,改成setter注入的配置方式:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="testA" class="org.springframework.beans.factory.myspring.circle.TestA"> <property name="testB" ref="testB"/> bean> <bean id="testB" class="org.springframework.beans.factory.myspring.circle.TestB"> <property name="testC" ref="testC"/> bean> <bean id="testC" class="org.springframework.beans.factory.myspring.circle.TestC"> <property name="testA" ref="testA"/> bean>beans>
测试类同样同样也修改下:
package org.springframework.beans.factory.myspring;import org.junit.Test;import org.junit.rules.ExpectedException;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.BeanCurrentlyInCreationException;import org.springframework.beans.factory.BeanFactory;import org.springframework.beans.factory.myspring.circle.TestA;import org.springframework.beans.factory.xml.XmlBeanFactory;import org.springframework.core.io.ClassPathResource;/** * @Authror ayo * @Date 2020/11/16 17:52 */public class BeanFactoryTest { private static final Logger logger = LoggerFactory.getLogger(BeanFactoryTest.class); /** * 测试setter的循环依赖 */ @Test public void testCircleBySetter() { try { XmlBeanFactory xmlBeanFactory = new XmlBeanFactory(new ClassPathResource("org/springframework/beans/factory/myspring/beanFactoryTest.xml")); TestA testA = (TestA) xmlBeanFactory.getBean("testA"); System.out.println("testA=" + testA); }catch (Exception e){ logger.error("testCircleByConstructor error==>" + e.getMessage(), e); } }}
运行下代码:
@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; }
你分析下,创建testA,创建testB,创建testC的时候,能拿到sharedInstance吗(就是上面这个方法返回的),肯定拿不到啊,为什么?因为只有bean快创建完后才会放入缓存的啊,也就是说testA,testB,testC创建完后才会放入缓存,这时候testC创建完成了,发现依赖属性testA,然后去注入属性testA,就是又走doGetBean获取testA去了,这时候关键点来了,看下上面的截图,去获取sharedInstance的时候,看下这个缓存中的取值,来个视频:
看见没,其实是从singletonFactories这个缓存中获取到了创建这个bean的工厂,然后走AbstractBeanFactory#getObjectForBeanInstance方法去获取testA,获取完后放入了earlySingletonObjects缓存,同时将创建这个testA的工厂从缓存中删除,注意,这时候这个testA是个半成品,sharedInstance返回不为空的话就不会再走下面的getSingleton去创建了(getSingleton中有一步beforeSingletonCreation就是校验传进来的bean是否正在被创建,很显然testA正在被创建,这样就会抛出循环依赖的异常),这样就解决了循环依赖!!
好了,循环依赖基本上就讲完了,总结下,spring容器在构造器注入的时候是无法解决循环依赖的,而在setter注入的时候可以解决,但前提scope是singleton的,因为prototype的都没有三级缓存,肯定解决不了的。还是那句话,一定要自己去debug试下,这样你才能真正的理解整个过程。