循环依赖就是循环引用,就是两个或多个Bean相互之间的持有对方,比如CircleA引用CircleB,CircleB引用CircleC,CircleC引用CircleA,则它们最终反映为一个环。此处不是循环调用,循环调用是方法之间的环调用。

    循环调用是无法解决的,除非有终结条件,否则就是死循环,最终导致内存溢出错误。

    Spring容器循环依赖包括构造器循环依赖和setter循环依赖,那Spring容器如何解决循环依赖呢?首先让我们来定义循环引用类:

package cn.javass.spring.chapter3.bean;
public class CircleA {
    private CircleB circleB;
    public CircleA() {
    }
    public CircleA(CircleB circleB) {
        this.circleB = circleB;
    }
public void setCircleB(CircleB circleB)
{
        this.circleB = circleB;
    }
public void a() {
   circleB.b();
}
}
package cn.javass.spring.chapter3.bean;
public class CircleB {
    private CircleC circleC;
    public CircleB() {
    }
    public CircleB(CircleC circleC) {
        this.circleC = circleC;
    }
public void setCircleC(CircleC circleC)
{
        this.circleC = circleC;
    }
    public void b() {
        circleC.c();
    }
}
package cn.javass.spring.chapter3.bean;
public class CircleC {
    private CircleA circleA;
    public CircleC() {
    }
    public CircleC(CircleC circleC) {
        this.circleC = circleC;
    }
public void setCircleC(CircleC circleC)
{
        this.circleC = circleC;
    }
    public void b() {
        circleC.c();
    }
}

<bean id="circleA" class="cn.javass.spring.chapter3.bean.CircleA">
<constructor-arg index="0" ref="circleB"/>
</bean>
<bean id="circleB" class="cn.javass.spring.chapter3.bean.CircleB">
<constructor-arg index="0" ref="circleC"/>
</bean>
<bean id="circleC" class="cn.javass.spring.chapter3.bean.CircleC">
<constructor-arg index="0" ref="circleA"/>
</bean>
写段测试代码(cn.javass.spring.chapter3.CircleTest)测试一下吧:
@Test(expected = BeanCurrentlyInCreationException.class)
public void testCircleByConstructor() throws Throwable {
try {
      new ClassPathXmlApplicationContext("chapter3/circleInjectByConstructor.xml");
    }
    catch (Exception e) {
      //因为要在创建circle3时抛出;
      Throwable e1 = e.getCause().getCause().getCause();
      throw e1;
    }
}

让我们分析一下吧:

1、Spring容器创建“circleA” Bean,首先去“当前创建Bean池”查找是否当前Bean正在创建,如果没发现,则继续准备其需要的构造器参数“circleB”,并将“circleA” 标识符放到“当前创建Bean池”;

2、Spring容器创建“circleB” Bean,首先去“当前创建Bean池”查找是否当前Bean正在创建,如果没发现,则继续准备其需要的构造器参数“circleC”,并将“circleB” 标识符放到“当前创建Bean池”;

3、Spring容器创建“circleC” Bean,首先去“当前创建Bean池”查找是否当前Bean正在创建,如果没发现,则继续准备其需要的构造器参数“circleA”,并将“circleC” 标识符放到“当前创建Bean池”;

4、到此为止Spring容器要去创建“circleA”Bean,发现该Bean 标识符在“当前创建Bean池”中,因为表示循环依赖,抛出BeanCurrentlyInCreationException。

二、setter循环依赖:表示通过setter注入方式构成的循环依赖。

对于setter注入造成的依赖是通过Spring容器提前暴露刚完成构造器注入但未完成其他步骤(如setter注入)的Bean来完成的,而且只能解决单例作用域的Bean循环依赖。

如下代码所示,通过提前暴露一个单例工厂方法,从而使其他Bean能引用到该Bean。
addSingletonFactory(beanName, new ObjectFactory() {
    public Object getObject() throws BeansException {
        return getEarlyBeanReference(beanName, mbd, bean);
    }
});
具体步骤如下:

       1、Spring容器创建单例“circleA” Bean,首先根据无参构造器创建Bean,并暴露一个“ObjectFactory ”用于返回一个提前暴露一个创建中的Bean,并将“circleA” 标识符放到“当前创建Bean池”;然后进行setter注入“circleB”;

       2、Spring容器创建单例“circleB” Bean,首先根据无参构造器创建Bean,并暴露一个“ObjectFactory”用于返回一个提前暴露一个创建中的Bean,并将“circleB” 标识符放到“当前创建Bean池”,然后进行setter注入“circleC”;

       3、Spring容器创建单例“circleC” Bean,首先根据无参构造器创建Bean,并暴露一个“ObjectFactory ”用于返回一个提前暴露一个创建中的Bean,并将“circleC” 标识符放到“当前创建Bean池”,然后进行setter注入“circleA”;进行注入“circleA”时由于提前暴露了“ObjectFactory”工厂从而使用它返回提前暴露一个创建中的Bean;

4、最后在依赖注入“circleB”和“circleA”,完成setter注入。

 

对于“prototype”作用域Bean,Spring容器无法完成依赖注入,因为“prototype”作用域的Bean,Spring容器不进行缓存,因此无法提前暴露一个创建中的Bean。
<!-- 定义Bean配置文件,注意scope都是“prototype”-->
<bean id="circleA" class="cn.javass.spring.chapter3.bean.CircleA" scope="prototype">
        <property name="circleB" ref="circleB"/>
   </bean>
   <bean id="circleB" class="cn.javass.spring.chapter3.bean.CircleB" scope="prototype">
       <property name="circleC" ref="circleC"/>
   </bean>
   <bean id="circleC" class="cn.javass.spring.chapter3.bean.CircleC" scope="prototype">
       <property name="circleA" ref="circleA"/>
   </bean>

//测试代码cn.javass.spring.chapter3.CircleTest
@Test(expected = BeanCurrentlyInCreationException.class)
public void testCircleBySetterAndPrototype () throws Throwable {
    try {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(
"chapter3/circleInjectBySetterAndPrototype.xml");
        System.out.println(ctx.getBean("circleA"));
    }
    catch (Exception e) {
        Throwable e1 = e.getCause().getCause().getCause();
        throw e1;
    }
}
对于“singleton”作用域Bean,可以通过“setAllowCircularReferences(false);”来禁用循环引用:

@Test(expected = BeanCurrentlyInCreationException.class)
public void testCircleBySetterAndSingleton2() throws Throwable {
    try {
        ClassPathXmlApplicationContext ctx =
new ClassPathXmlApplicationContext();
        ctx.setConfigLocation("chapter3/circleInjectBySetterAndSingleton.xml");
        ctx.refresh();
    }
    catch (Exception e) {
        Throwable e1 = e.getCause().getCause().getCause();
        throw e1;
    }
}

以下是spring创建bean的部分过程源代码,也论证了上述分析

//创建Bean实例对象 
protected Object createBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) 
            throws BeanCreationException { 
        if (logger.isDebugEnabled()) { 
            logger.debug("Creating instance of bean '" + beanName + "'"); 
        } 
        //判断需要创建的Bean是否可以实例化,即是否可以通过当前的类加载器加载 
        resolveBeanClass(mbd, beanName); 
        //校验和准备Bean中的方法覆盖 
        try { 
            mbd.prepareMethodOverrides(); 
        } 
        catch (BeanDefinitionValidationException ex) { 
            throw new BeanDefinitionStoreException(mbd.getResourceDescription(), 
                    beanName, "Validation of method overrides failed", ex); 
        } 
        try { 
//如果Bean配置了初始化前和初始化后的处理器,则试图返回一个需要创建//Bean的代理对象 
            Object bean = resolveBeforeInstantiation(beanName, mbd); 
            if (bean != null) { 
                return bean; 
            } 
        } 
        catch (Throwable ex) { 
            throw new BeanCreationException(mbd.getResourceDescription(), beanName, 
                    "BeanPostProcessor before instantiation of bean failed", ex); 
        } 
        //创建Bean的入口 
        Object beanInstance = doCreateBean(beanName, mbd, args); 
        if (logger.isDebugEnabled()) { 
            logger.debug("Finished creating instance of bean '" + beanName + "'"); 
        } 
        return beanInstance; 
    } 
//真正创建Bean的方法 
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) { 
        //封装被创建的Bean对象 
        BeanWrapper instanceWrapper = null; 
        if (mbd.isSingleton()){//单态模式的Bean,先从容器中缓存中获取同名Bean 
            instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); 
        } 
        if (instanceWrapper == null) { 
            //创建实例对象 
            instanceWrapper = createBeanInstance(beanName, mbd, args); 
        } 
        final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null); 
        //获取实例化对象的类型 
        Class beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null); 
        //调用PostProcessor后置处理器 
        synchronized (mbd.postProcessingLock) { 
            if (!mbd.postProcessed) { 
                applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); 
                mbd.postProcessed = true; 
            } 
        } 
        // Eagerly cache singletons to be able to resolve circular references 
        //向容器中缓存单态模式的Bean对象,以防循环引用 
        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && 
                isSingletonCurrentlyInCreation(beanName)); 
        if (earlySingletonExposure) { 
            if (logger.isDebugEnabled()) { 
                logger.debug("Eagerly caching bean '" + beanName + 
                        "' to allow for resolving potential circular references"); 
            } 
            //这里是一个匿名内部类,为了防止循环引用,尽早持有对象的引用 
            addSingletonFactory(beanName, new ObjectFactory() { 
                public Object getObject() throws BeansException { 
                    return getEarlyBeanReference(beanName, mbd, bean); 
                } 
            }); 
        } 
        //Bean对象的初始化,依赖注入在此触发 
        //这个exposedObject在初始化完成之后返回作为依赖注入完成后的Bean 
        Object exposedObject = bean; 
        try { 
            //将Bean实例对象封装,并且Bean定义中配置的属性值赋值给实例对象 
            populateBean(beanName, mbd, instanceWrapper); 
            if (exposedObject != null) { 
                //初始化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) { 
            //获取指定名称的已注册的单态模式Bean对象 
            Object earlySingletonReference = getSingleton(beanName, false); 
            if (earlySingletonReference != null) { 
                //根据名称获取的以注册的Bean和正在实例化的Bean是同一个 
                if (exposedObject == bean) { 
                    //当前实例化的Bean初始化完成 
                    exposedObject = earlySingletonReference; 
                } 
                //当前Bean依赖其他Bean,并且当发生循环引用时不允许新创建实例对象 
                else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { 
                    String[] dependentBeans = getDependentBeans(beanName); 
                    Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length); 
                    //获取当前Bean所依赖的其他Bean 
                    for (String dependentBean : dependentBeans) { 
                        //对依赖Bean进行类型检查 
                        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."); 
                    } 
                } 
            } 
        } 
        //注册完成依赖注入的Bean 
        try { 
            registerDisposableBeanIfNecessary(beanName, bean, mbd); 
        } 
        catch (BeanDefinitionValidationException ex) { 
            throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); 
        } 
        return exposedObject; 
    } 

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐