spring 解决循环依赖

在看面试题的时候忽然想到spring 是怎么解决相互依赖的问题的?模糊不清,赶忙记录下来。java

必定要将bean的实例化(beanFactory完成的)和完成bean的建立(ObjectFactory返回的)区分开,bean的实例化仅仅是得到了bean的实例,该bean仍在继续建立之中,以后在该bean实例的基础之上,还要作不少额外的操做,例如bean的属性填充、处理器的应用、bean的循环依赖解决等,怎么解决循环依赖的问题?面试

当ClassA引用ClassB,ClassB又引用ClassA,那么两个类之间就会造成一个闭环,致使循环依赖的出现。你们只需记住一点,Spring只能解决单例模式下的Setter循环依赖。spring

1.测试用例
bean和xml
package com.lyc.cn.v2.day01.cycle;缓存

/**
 * @author: LiYanChao
 * @create: 2018-10-16 23:59
 */
public class ClassA {
    private ClassB classB;app

    public ClassB getClassB() {
        return classB;
    }ide

    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;post

    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>

结果
========测试方法开始=======this

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 {
        // 一、解析引用beanName
        Object bean;
        String refName = ref.getBeanName();
        refName = String.valueOf(doEvaluate(refName));
        // 二、判断引用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);
        }
        // 三、从当前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) {
    // 一、从缓存中获取bean
    Object singletonObject = this.singletonObjects.get(beanName);
    // 二、未能获取到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的循环依赖问题。

三.总结
一、Spring只能解决Setter方法注入的【单例】bean之间的循环依赖
二、ClassA依赖ClassB,ClassB又依赖ClassA,造成依赖闭环。Spring在获取ClassA的实例时,发现ClassA在1级和2级缓存中不存在,会先在3级缓存中优先添加(曝光)   此时ClassA并未完成建立,就将其曝光加入正在建立的bean缓存中。

在解析ClassA的属性时,又发现依赖于ClassB,再次去获取ClassB(建立B的过程与A一致),当解析ClassB的属性时,又发现须要ClassA的属性,但此时的ClassA已经被提早曝光加入了正在建立的bean的缓存中(spring的3级缓存,其实就是3个map),则无需建立新的的ClassA的实例,直接从缓存中获取便可,完成B对象中A属性的注入流程,进而整个B的对象建立完成,return 给A一个B对象,A再进行注入并完成建立。从而解决循环依赖问题。

三、spring 的3级缓存

/** 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);