spring如何解决单例循环依赖问题?
1、spring循环依赖场景
2、循环依赖解决方式: 三级缓存
循环依赖的产生可能有很多种情况,例如:
- A的构造方法中依赖了B的实例对象,同时B的构造方法中依赖了A的实例对象
- A的构造方法中依赖了B的实例对象,同时B的某个field或者setter需要A的实例对象,以及反之
- A的某个field或者setter依赖了B的实例对象,同时B的某个field或者setter依赖了A的实例对象,以及反之
Spring对于循环依赖的解决不是无条件的,首先前提条件是针对scope单例并且允许解决循环依赖的对象。以上三种情况: 第一种Spring无法解决, 第二种只能解决一部分情况, 第三种可以解决
以下为对应的示例demo:
1 public class CirculationA { 2 3 private CirculationB circulationB; 4 5 public CirculationA(CirculationB circulationB) { 6 this.circulationB = circulationB; 7 } 8 9 public CirculationA() { 10 } 11 12 public CirculationB getCirculationB() { 13 return circulationB; 14 } 15 16 public void setCirculationB(CirculationB circulationB) { 17 this.circulationB = circulationB; 18 } 19 } 20 21 public class CirculationB { 22 private CirculationA circulationA; 23 24 public CirculationB(CirculationA circulationA) { 25 this.circulationA = circulationA; 26 } 27 public CirculationB() { 28 } 29 30 public CirculationA getCirculationA() { 31 return circulationA; 32 } 33 34 public void setCirculationA(CirculationA circulationA) { 35 this.circulationA = circulationA; 36 } 37 }
ioc-CirculationReference.xml
<?xml version="1.0" encoding="utf-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <!--使用构造函数互相引用 --> <bean id="circulationb" class="com.nancy.ioc.CirculationReference.CirculationB" > <constructor-arg name="circulationA" ref="circulationa"/> </bean> <bean id="circulationa" class="com.nancy.ioc.CirculationReference.CirculationA" > <constructor-arg name="circulationB" ref="circulationb"/> </bean> </beans>
CirculationReferenceTest代码
1 public class CirculationReferenceTest { 2 private ApplicationContext applicationContext ; 3 4 @Before 5 public void beforeApplicationContext(){ 6 applicationContext = new ClassPathXmlApplicationContext("ioc-CirculationReference.xml") ; 7 } 8 9 @Test 10 public void test(){ 11 12 } 13 14 @After 15 public void after(){ 16 if(applicationContext != null){ 17 ((ClassPathXmlApplicationContext)applicationContext).close(); 18 } 19 } 20 }
错误堆栈信息: 验证了spring无法解决第一种循环依赖
1 org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'circulationb' defined in class path resource [ioc-CirculationReference.xml]: Cannot resolve reference to bean 'circulationa' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'circulationa' defined in class path resource [ioc-CirculationReference.xml]: Cannot resolve reference to bean 'circulationb' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'circulationb': Requested bean is currently in creation: Is there an unresolvable circular reference? 2 3 at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:378) 4 at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:110) 5 at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:676) 6 at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:188) 7 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1308) 8 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1154) 9 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:538) 10 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:498) 11 at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) 12 at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) 13 at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) 14 at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) 15 at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:846) 16 at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:863) 17 at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:546) 18 at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:144) 19 at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:85) 20 at com.nancy.ioc.CirculationReferenceTest.beforeApplicationContext(CirculationReferenceTest.java:18) 21 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 22 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 23 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 24 at java.lang.reflect.Method.invoke(Method.java:498) 25 at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) 26 at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) 27 at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) 28 at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24) 29 at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27) 30 at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) 31 at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) 32 at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) 33 at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) 34 at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) 35 at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) 36 at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) 37 at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) 38 at org.junit.runners.ParentRunner.run(ParentRunner.java:363) 39 at org.junit.runner.JUnitCore.run(JUnitCore.java:137) 40 at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) 41 at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47) 42 at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) 43 at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70) 44 Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'circulationa' defined in class path resource [ioc-CirculationReference.xml]: Cannot resolve reference to bean 'circulationb' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'circulationb': Requested bean is currently in creation: Is there an unresolvable circular reference? 45 at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:378) 46 at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:110) 47 at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:676) 48 at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:188) 49 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1308) 50 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1154) 51 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:538) 52 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:498) 53 at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) 54 at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) 55 at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) 56 at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) 57 at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:367) 58 ... 40 more 59 Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'circulationb': Requested bean is currently in creation: Is there an unresolvable circular reference? 60 at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:339) 61 at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:215) 62 at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) 63 at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) 64 at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:367) 65 ... 52 more
修改对应的ioc-CirculationReference.xml如下并再次运行: 验证第二种情况, 此时运行结果正常.
<?xml version="1.0" encoding="utf-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <!-- B的filed依赖A A构造函数依赖B --> <bean id="circulationb" class="com.nancy.ioc.CirculationReference.CirculationB" > <property name="circulationA" ref="circulationa"/> </bean> <bean id="circulationa" class="com.nancy.ioc.CirculationReference.CirculationA" > <constructor-arg name="circulationB" ref="circulationb"/> </bean> </beans>
如果将 circulationa 和 circulationb 在ioc-CirculationReference.xml文件声明的顺序调换, 使用构造的circulationa先加载. 再次报循环依赖无法解析 为什么会出现这样的情况呢?
第一个demo标红的错误堆栈部分信息清晰的看出bean创建基本流程, 由refresh()为入口切入, 这里只分析单例bean创建流程:
1)、AbstractBeanFactory.getBean为入口 并委托 AbstractBeanFactory.doGetBean创建
2)、AbstractBeanFactory.doGetBean 会首先从AbstractBeanFactory.getSingleton中获取缓存的bean对象, 如果不存在则调用抽象方法createBean, 即子类实现的AbstractAutowireCapableBeanFactory.createBean方法
3)、AbstractAutowireCapableBeanFactory.createBean方法触发doCreateBean依次调用以下方法实现bean创建过程
- createBeanInstance: 实例化bean, 如果需要依赖其他对象则首先创建其他对象(发生循环依赖的地方)
- addSingletonFactory: 将实例化bean加入三级缓存
- populateBean: 初始化bean, 如果需要依赖其他对象则首先创建其他对象(发生循环依赖的地方)
- initializeBean
- registerDisposableBeanIfNecessary
4)、AbstractAutowireCapableBeanFactory.autowireConstructor使用构造函数进行实例化
5)、最终调用 ConstructorResolver.autowireConstructor 和 ConstructorResolver.resolveConstructorArguments 进行实例化已经解析构造参数
6)、调用BeanDefinitionValueResolver.resolveValueIfNecessary 和 BeanDefinitionValueResolver.resolveReference 模版类解析构造参数
这里只分析流程主干代码:
AbstractBeanFactory为bean创建的入口
1 @Override 2 public Object getBean(String name) throws BeansException { 3 return doGetBean(name, null, null, false); 4 } 5 6 protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, 7 @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { 8 9 final String beanName = transformedBeanName(name); 10 Object bean; 11 12 // Eagerly check singleton cache for manually registered singletons. 13 // 14 Object sharedInstance = getSingleton(beanName); 15 if (sharedInstance != null && args == null) { 16 if (logger.isTraceEnabled()) { 17 if (isSingletonCurrentlyInCreation(beanName)) { 18 logger.trace("Returning eagerly cached instance of singleton bean '" + beanName + 19 "' that is not fully initialized yet - a consequence of a circular reference"); 20 } 21 else { 22 logger.trace("Returning cached instance of singleton bean '" + beanName + "'"); 23 } 24 } 25 bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); 26 } 27 28 else { 29 // Fail if we're already creating this bean instance: 30 // We're assumably within a circular reference. 31 if (isPrototypeCurrentlyInCreation(beanName)) { 32 throw new BeanCurrentlyInCreationException(beanName); 33 } 34 35 //......省略...... 36 //......省略...... 37 38 try { 39 //......省略...... 40 41 // Create bean instance. 42 if (mbd.isSingleton()) { 43 sharedInstance = getSingleton(beanName, () -> { 44 try { 45 return createBean(beanName, mbd, args); 46 } 47 catch (BeansException ex) { 48 // Explicitly remove instance from singleton cache: It might have been put there 49 // eagerly by the creation process, to allow for circular reference resolution. 50 // Also remove any beans that received a temporary reference to the bean. 51 destroySingleton(beanName); 52 throw ex; 53 } 54 }); 55 bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); 56 } 57 58 //........ 59 } 60 61 62 protected abstract Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) 63 throws BeanCreationException;
在DefaultSingletonBeanRegistry使用三级缓存:
// 第一层: 初始化完备的单例bean /** Cache of singleton objects: bean name to bean instance. */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); // 第二层: 提前暴光的单例对象的Cache /** Cache of early singleton objects: bean name to bean instance. */ private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); // 第三层: ObjectFactory工厂bean缓存, 存储实例话后的bean Factory /** Cache of singleton factories: bean name to ObjectFactory. */ private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
从缓存中获取单例对象
1 protected Object getSingleton(String beanName, boolean allowEarlyReference) { 2 // 首先从第一层缓存获取 3 Object singletonObject = this.singletonObjects.get(beanName); 4 // 其次第一层未找到缓存 且 bean处于创建中(例如A定义的构造函数依赖了B对象,得先去创建B对象,或者在populatebean过程中依赖了B对象,得先去创建B对象,此时A处于创建中) 5 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { 6 synchronized (this.singletonObjects) { 7 singletonObject = this.earlySingletonObjects.get(beanName); 8 // 最后第二层未找到缓存 并 允许循环依赖即从工厂类获取对象 9 if (singletonObject == null && allowEarlyReference) { 10 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); 11 if (singletonFactory != null) { 12 singletonObject = singletonFactory.getObject(); 13 // 此时会将三级缓存 移入 二级缓存 14 this.earlySingletonObjects.put(beanName, singletonObject); 15 this.singletonFactories.remove(beanName); 16 } 17 } 18 } 19 } 20 return (singletonObject != NULL_OBJECT ? singletonObject : null); 21 }
创建并缓存单例对象: 创建过程中会暂时先标记bean为创建中, 创建完成之后会清楚该标记并加入第一级缓存
// 创建并注册单例对象 如果存在直接返回 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 + "'"); } // 标记创建开始, 用于标记创建中 多次创建会抛出BeanCurrentlyInCreationException 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; } }
标记函数, 重复操作会抛出异常, 循环依赖不能解析抛出异常的触发点
1 /** 2 * Callback before singleton creation. 3 * <p>The default implementation register the singleton as currently in creation. 4 * @param beanName the name of the singleton about to be created 5 * @see #isSingletonCurrentlyInCreation 6 */ 7 protected void beforeSingletonCreation(String beanName) { 8 if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { 9 throw new BeanCurrentlyInCreationException(beanName); 10 } 11 } 12 13 /** 14 * Callback after singleton creation. 15 * <p>The default implementation marks the singleton as not in creation anymore. 16 * @param beanName the name of the singleton that has been created 17 * @see #isSingletonCurrentlyInCreation 18 */ 19 protected void afterSingletonCreation(String beanName) { 20 if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) { 21 throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation"); 22 } 23 }
AbstractAutowireCapableBeanFactory中doCreateBean: 核心createBeanInstance、addSingletonFactory、populateBean
1 protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) 2 throws BeanCreationException { 3 4 // Instantiate the bean. 5 BeanWrapper instanceWrapper = null; 6 if (mbd.isSingleton()) { 7 instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); 8 } 9 // 创建bean实例化, 此时bean并未进行 10 if (instanceWrapper == null) { 11 instanceWrapper = createBeanInstance(beanName, mbd, args); 12 } 13 //......... 14 15 // bean为单例并 允许循环依赖 且 处于创建中 加入3级缓存 16 // Eagerly cache singletons to be able to resolve circular references 17 // even when triggered by lifecycle interfaces like BeanFactoryAware. 18 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && 19 isSingletonCurrentlyInCreation(beanName)); 20 if (earlySingletonExposure) { 21 if (logger.isTraceEnabled()) { 22 logger.trace("Eagerly caching bean '" + beanName + 23 "' to allow for resolving potential circular references"); 24 } 25 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); 26 } 27 28 // Initialize the bean instance. 29 Object exposedObject = bean; 30 try { 31 // 对bean属性进行复制 32 populateBean(beanName, mbd, instanceWrapper); 33 // 调用初始化方法 34 exposedObject = initializeBean(beanName, exposedObject, mbd); 35 } 36 catch (Throwable ex) { 37 if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) { 38 throw (BeanCreationException) ex; 39 } 40 else { 41 throw new BeanCreationException( 42 mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); 43 } 44 } 45 46 if (earlySingletonExposure) { 47 Object earlySingletonReference = getSingleton(beanName, false); 48 if (earlySingletonReference != null) { 49 if (exposedObject == bean) { 50 exposedObject = earlySingletonReference; 51 } 52 else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { 53 String[] dependentBeans = getDependentBeans(beanName); 54 Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); 55 for (String dependentBean : dependentBeans) { 56 if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { 57 actualDependentBeans.add(dependentBean); 58 } 59 } 60 if (!actualDependentBeans.isEmpty()) { 61 throw new BeanCurrentlyInCreationException(beanName, 62 "Bean with name '" + beanName + "' has been injected into other beans [" + 63 StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + 64 "] in its raw version as part of a circular reference, but has eventually been " + 65 "wrapped. This means that said other beans do not use the final version of the " + 66 "bean. This is often the result of over-eager type matching - consider using " + 67 "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example."); 68 } 69 } 70 } 71 } 72 73 // Register bean as disposable. 74 try { 75 registerDisposableBeanIfNecessary(beanName, bean, mbd); 76 } 77 catch (BeanDefinitionValidationException ex) { 78 throw new BeanCreationException( 79 mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); 80 } 81 82 return exposedObject; 83 } 84 85 protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { 86 Assert.notNull(singletonFactory, "Singleton factory must not be null"); 87 synchronized (this.singletonObjects) { 88 if (!this.singletonObjects.containsKey(beanName)) { 89 this.singletonFactories.put(beanName, singletonFactory); 90 this.earlySingletonObjects.remove(beanName); 91 this.registeredSingletons.add(beanName); 92 } 93 } 94 }
ConstructorResolver
public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd, @Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) { // ........ int minNrOfArgs; if (explicitArgs != null) { minNrOfArgs = explicitArgs.length; } else { ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues(); resolvedValues = new ConstructorArgumentValues(); minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues); } // ........ } private int resolveConstructorArguments(String beanName, RootBeanDefinition mbd, BeanWrapper bw, ConstructorArgumentValues cargs, ConstructorArgumentValues resolvedValues) { //....... for (ConstructorArgumentValues.ValueHolder valueHolder : cargs.getGenericArgumentValues()) { if (valueHolder.isConverted()) { resolvedValues.addGenericArgumentValue(valueHolder); } else { Object resolvedValue = valueResolver.resolveValueIfNecessary("constructor argument", valueHolder.getValue()); ConstructorArgumentValues.ValueHolder resolvedValueHolder = new ConstructorArgumentValues.ValueHolder( resolvedValue, valueHolder.getType(), valueHolder.getName()); resolvedValueHolder.setSource(valueHolder); resolvedValues.addGenericArgumentValue(resolvedValueHolder); } } return minNrOfArgs; }
BeanDefinitionValueResolver
1 @Nullable 2 public Object resolveValueIfNecessary(Object argName, @Nullable Object value) { 3 // We must check each value to see whether it requires a runtime reference 4 // to another bean to be resolved. 5 if (value instanceof RuntimeBeanReference) { 6 RuntimeBeanReference ref = (RuntimeBeanReference) value; 7 return resolveReference(argName, ref); 8 } 9 //....... 10 } 11 12 @Nullable 13 private Object resolveReference(Object argName, RuntimeBeanReference ref) { 14 try { 15 Object bean; 16 String refName = ref.getBeanName(); 17 refName = String.valueOf(doEvaluate(refName)); 18 if (ref.isToParent()) { 19 if (this.beanFactory.getParentBeanFactory() == null) { 20 throw new BeanCreationException( 21 this.beanDefinition.getResourceDescription(), this.beanName, 22 "Can't resolve reference to bean '" + refName + 23 "' in parent factory: no parent factory available"); 24 } 25 bean = this.beanFactory.getParentBeanFactory().getBean(refName); 26 } 27 else { 28 bean = this.beanFactory.getBean(refName); 29 this.beanFactory.registerDependentBean(refName, this.beanName); 30 } 31 if (bean instanceof NullBean) { 32 bean = null; 33 } 34 return bean; 35 } 36 catch (BeansException ex) { 37 throw new BeanCreationException( 38 this.beanDefinition.getResourceDescription(), this.beanName, 39 "Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex); 40 } 41 }
首先需要明确spring对象如果拿不到构造参数将无法使用构造函数实例化, 所以如果构造函数依赖于其他对象必然先去创建其他对象. 如果是使用默认空参构造则可以实例化, 如果初始化阶段依赖于其他对象必然会去创建其他对象.
- 第一个demo情况, 当createBeanInstance使用构造函数创建circulationA需要依赖circulationB, 则会暂时停下来并会创建circulationB 并由于对应未创建并不会加入三级缓存. 当使用构造函数创建circulationB需要依赖circulationA, 则也会暂时停下来并会创建circulationA 并由于对应未创建并不会加入三级缓存. 当创建circulationA会再次调用beforeSingletonCreation进行标记, 因为会抛出BeanCurrentlyInCreationException异常终止ioc容器初始化. circulationA和circulationB 由于各自拿不到对应的构造函数参数而无法实例化
- 第二个demo情况, circulationB使用setter依赖circulationA, 因此createBeanInstance使用默认的空参数构造实例化, 完成之后加入三级缓存并在populateBean中属性进行初始化, 此时需要实例化circulationA. 当使用构造函数创建circulationA需要依赖circulationB, 则也会暂时停下来并去创建circulationB, 由于在缓存中拿到circulationB即完成circulationA实例化. 再次返回circulationB的populateBean方法. 此时circulationA 和 circulationB 加载完成. 由此可以类推, 如果只是通过属性 或者 setter方法进行循环依赖 spring可以完美解决.
- 在第二个demo情况中将 circulationB 和 circulationA 声明顺序进行交换依然导致了加载错误. 根源问题在于与第一种情况一样, circulationA拿不到对应的构造函数参数而无法实例化 未进入三级缓存, 故而导致了circulationB再次创建circulationA的时候, 由beforeSingletonCreation抛出BeanCurrentlyInCreationException异常终止ioc容器初始化.
因此得出结果spring解决的循环依赖只是部分, 而无法解决的情况是在使用构造函数互相引用的场景. spring bean声明中应避免第一种情况 和 第一种情况的变种情况.
参考链接:
Spring源码初探-IOC(4)-Bean的初始化-循环依赖的解决
Spring-bean的循环依赖以及解决方式