通过前几节的分析,已经成功将bean实例化,但是大家一定要将bean的实例化和完成bean的创建区分开,bean的实例化仅仅是获得了bean的实例,该bean仍在继续创建之中,之后在该bean实例的基础之上,还要做很多额外的操作,例如bean的属性填充、处理器的应用、bean的循环依赖解决等,今天我们就来分析下Spring是如何解决bean之间的循环依赖。
当ClassA引用ClassB,ClassB又引用ClassA,那么两个类之间就会形成一个闭环,导致循环依赖的出现。大家只需记住一点,Spring只能解决单例模式下的Setter循环依赖。
1.测试用例
bean和xml
package com.lyc.cn.v2.day01.cycle; /** * @author: LiYanChao * @create: 2018-10-16 23:59 */ public class ClassA { private ClassB classB; public ClassB getClassB() { return classB; } public void setClassB(ClassB classB) { this.classB = classB; } }
package com.lyc.cn.v2.day01.cycle; /** * @author: LiYanChao * @create: 2018-10-16 23:59 */ public class ClassB { private ClassA classA; public ClassA getClassA() { return classA; } public void setClassA(ClassA classA) { this.classA = classA; } }
<!--循环依赖--> <bean id="classA" class="com.lyc.cn.v2.day01.cycle.ClassA" scope="singleton"> <property name="classB" ref="classB"></property> </bean> <bean id="classB" class="com.lyc.cn.v2.day01.cycle.ClassB" scope="singleton"> <property name="classA" ref="classA"></property> </bean>
结果
========测试方法开始=======
com.lyc.cn.v2.day01.cycle.ClassB@2d6a9952
com.lyc.cn.v2.day01.cycle.ClassA@22a71081
========测试方法结束=======
当scope="singleton"时结果是正常的,Spring为我们解决了bean之间的循环依赖,再将scope改为prototype,运行测试用例(摘取部分异常信息):
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'classA': Requested bean is currently in creation: Is there an unresolvable circular reference? at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:255) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:198) at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:372) ... 40 more
从异常信息中可以看到Is there an unresolvable circular reference?,有循环依赖异常,这也证明了Spring是不能解决prototype作用域的bean之间的循环依赖的。
下面我们从源码角度去分析,Spring是如何解决bean之间的循环依赖问题的。
引
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { // Instantiate the bean. // ① 实例化bean BeanWrapper instanceWrapper = null; // 注意factoryBeanInstanceCache是ConcurrentMap,remove方法会返回删除的键值(如果不存在返回null) if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } // 如果factoryBeanInstanceCache没有缓存对应的BeanWrapper,则重新创建bean实例 if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } final Object bean = instanceWrapper.getWrappedInstance(); Class<?> beanType = instanceWrapper.getWrappedClass(); if (beanType != NullBean.class) { mbd.resolvedTargetType = beanType; } // Allow post-processors to modify the merged bean definition. // ② 允许MergedBeanDefinitionPostProcessor后处理器修改已合并的bean定义。 synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { try { applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex); } mbd.postProcessed = true; } } // Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces like BeanFactoryAware. // ③ 提前缓存ObjectFactory以解决bean之间的循环依赖 // mbd.isSingleton()->是否单例,Spring只解决单例bean的循环依赖问题 // allowCircularReferences->是否允许循环依赖 // isSingletonCurrentlyInCreation->该bean是否创建中 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // Initialize the bean instance. // ④ 初始化bean实例 这里大家要与第①步区分开,到这里bean已经完成了实例化,但是还没有完成初始化的操作,例如bean的属性填充 Object exposedObject = bean; try { // 填充bean属性 populateBean(beanName, mbd, instanceWrapper); // 初始化bean exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) { throw (BeanCreationException) ex; } else { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); } } // ⑤ 循环依赖检查 if (earlySingletonExposure) { Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { exposedObject = earlySingletonReference; } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); for (String dependentBean : dependentBeans) { if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } if (!actualDependentBeans.isEmpty()) { throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been " + "wrapped. This means that said other beans do not use the final version of the " + "bean. This is often the result of over-eager type matching - consider using " + "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example."); } } } } // Register bean as disposable. try { // ⑥ 根据bean的作用域注册bean registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); } // ⑦ 返回bean实例 return exposedObject; }
通过第一步已经获得了bean的实例(第二步留在以后再讲解),直接看第三步:提前缓存ObjectFactory以解决bean之间的循环依赖。
1.提前曝光对象
这里涉及到一个非常重要的接口ObjectFactory,该接口是一个函数式接口且只有一个方法:T getObject() throws BeansException;,该方法用于返回一个bean的实例,此时的bean已经完成初始化,但是尚未完成创建。
如果当前的bean满足条件,则将当前正在创建的bean和其ObjectFactory对象提前曝光,加入到正在创建bean池中。
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { synchronized (this.singletonObjects) { if (!this.singletonObjects.containsKey(beanName)) { this.singletonFactories.put(beanName, singletonFactory); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } } }
2.循环依赖的解决
在完成bean的实例创建之后,还要填充bean的属性,针对ClassA,其属性是ClassB,如果要填充ClassA的属性则势必先要实例化ClassB,那么这里又涉及到一个概念,RuntimeBeanReference–>运行时引用。
打开BeanDefinitionValueResolver类的resolveValueIfNecessary方法。摘取代码片段(该方法会在以后全部分析)
判断RuntimeBeanReference属性
// ① RuntimeBeanReference->运行时引用 // 例如BeanA依赖BeanB,那么在配置文件中有通过配置ref标签进行引用的,在解析BeanDefinition的时候,是不会直接实例化BeanB的,那么这个引用就是RuntimeBeanReference if (value instanceof RuntimeBeanReference) { RuntimeBeanReference ref = (RuntimeBeanReference) value; return resolveReference(argName, ref); }
解析RuntimeBeanReference(运行时引用)
private Object resolveReference(Object argName, RuntimeBeanReference ref) { try { // 1、解析引用beanName Object bean; String refName = ref.getBeanName(); refName = String.valueOf(doEvaluate(refName)); // 2、判断引用bean是否属于父BeanFactory 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); } // 3、从当前beanFactory获取引用beanName实例 else { 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); } }
该过程很简单,首先解析refBeanName,然后通过getBean方法获取其实例,此时当前创建的bean是ClassA,引用bean是ClassB。
获取到ClassB实例之后,又要填充ClassB的属性,此时又会出现对RuntimeBeanReference的解析,即ClassA,再去获取ClassA的实例,此时的ClassA的实例已经被提前曝光,会从缓存中获取ClassA的实例。
protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 1、从缓存中获取bean Object singletonObject = this.singletonObjects.get(beanName); // 2、未能获取到bean,但是允许对当前创建的单例的早期引用(解决循环引用) // isSingletonCurrentlyInCreation-->判断指定的单例bean是否当前正在创建(Spring只解决单例bean的循环依赖问题) if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { // 从earlySingletonObjects获取提前曝光的bean singletonObject = this.earlySingletonObjects.get(beanName); // 未能获取到提前曝光的bean且当前的bean允许被创建早期依赖 if (singletonObject == null && allowEarlyReference) { // 从缓存中获取BeanFactory ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { // 通过getObject()方法获取提前曝光的bean singletonObject = singletonFactory.getObject(); // 将获取到的singletonObject缓存至earlySingletonObjects this.earlySingletonObjects.put(beanName, singletonObject); // 从singletonFactories移除bean this.singletonFactories.remove(beanName); } } } } return singletonObject; }
从singletonObjects中无法获取到bean的实例,因为此时bean尚未完成全部创建,但是由于我们提前曝光了ObjectFactory,所以通过singletonObject = singletonFactory.getObject();是可以获取到bean的实例的。这样就解决了Spring的循环依赖问题。
3.总结
Spring只能解决Setter方法注入的单例bean之间的循环依赖
ClassA依赖ClassB,ClassB又依赖ClassA,形成依赖闭环。Spring在获取ClassA的实例时,不等ClassA完成创建就将其曝光加入正在创建的bean缓存中。在解析ClassA的属性时,又发现依赖于ClassB,再次去获取ClassB,当解析ClassB的属性时,又发现需要ClassA的属性,但此时的ClassA已经被提前曝光加入了正在创建的bean的缓存中,则无需创建新的的ClassA的实例,直接从缓存中获取即可。从而解决循环依赖问题。