Spring循環(huán)依賴處理分析

循環(huán)依賴就是N個類中循環(huán)嵌套引用,如果在日常開發(fā)中我們用new 對象的方式發(fā)生這種循環(huán)依賴的話程序會在運行時一直循環(huán)調(diào)用,直至內(nèi)存溢出報錯,如下圖所示:


循環(huán)依賴的情況:

  • 構(gòu)造器參數(shù)依賴
  • setter方式singleton單例
  • setter方式prototype多例

1、構(gòu)造器參數(shù)依賴

Spring容器會將每一個正在創(chuàng)建的Bean 標(biāo)識符放在一個“當(dāng)前創(chuàng)建Bean池”中,Bean標(biāo)識符在創(chuàng)建過程中將一直保持在這個池中,因此如果在創(chuàng)建Bean過程中發(fā)現(xiàn)自己已經(jīng)在“當(dāng)前創(chuàng)建Bean池”里時將拋出
BeanCurrentlyInCreationException異常表示循環(huán)依賴;而對于創(chuàng)建完畢的Bean將從“當(dāng)前創(chuàng)建Bean池”中清除掉。

public class StudentA {  
  
    private StudentB studentB ;  
  
    public void setStudentB(StudentB studentB) {  
        this.studentB = studentB;  
    }  
  
    public StudentA() {  
    }  
      
    public StudentA(StudentB studentB) {  
        this.studentB = studentB;  
    }  
} 
public class StudentB {  
  
    private StudentC studentC ;  
  
    public void setStudentC(StudentC studentC) {  
        this.studentC = studentC;  
    }  
      
    public StudentB() {  
    }  
  
    public StudentB(StudentC studentC) {  
        this.studentC = studentC;  
    }  
} 
public class StudentC {  
  
    private StudentA studentA ;  
  
    public void setStudentA(StudentA studentA) {  
        this.studentA = studentA;  
    }  
  
    public StudentC() {  
    }  
   
    public StudentC(StudentA studentA) {  
        this.studentA = studentA;  
    }  
}  

OK,上面是很基本的3個類,,StudentA有參構(gòu)造是StudentB。StudentB的有參構(gòu)造是StudentC,StudentC的有參構(gòu)造是StudentA ,這樣就產(chǎn)生了一個循環(huán)依賴的情況,我們都把這三個Bean交給Spring管理,并用有參構(gòu)造實例化。

<bean id="a" class="com.zfx.student.StudentA">  
    <constructor-arg index="0" ref="b"></constructor-arg>  
</bean>  
<bean id="b" class="com.zfx.student.StudentB">  
    <constructor-arg index="0" ref="c"></constructor-arg>  
</bean>  
<bean id="c" class="com.zfx.student.StudentC">  
    <constructor-arg index="0" ref="a"></constructor-arg>  
</bean>

測試類:

public class Test {  
    public static void main(String[] args) {  
        ApplicationContext context = new ClassPathXmlApplicationContext("com/zfx/student/applicationContext.xml");  
        //System.out.println(context.getBean("a", StudentA.class));  
    }  
}  

執(zhí)行結(jié)果報錯:

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException:   
    Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference? 

報錯原因:
Spring容器先創(chuàng)建單例StudentA,StudentA依賴StudentB,然后將A放在“當(dāng)前創(chuàng)建Bean池”中,此時創(chuàng)建StudentB,StudentB依賴StudentC ,然后將B放在“當(dāng)前創(chuàng)建Bean池”中,此時創(chuàng)建StudentC,StudentC又依賴StudentA, 但是,此時Student已經(jīng)在池中,所以會報錯,,因為在池中的Bean都是未初始化完的,所以會依賴錯誤 ,(初始化完的Bean會從池中移除)

2、setter的singleton依賴

bean的生命周期如下圖:


2.1緩存的定義

先看DefaultSingletonBeanRegistry關(guān)于緩存的定義

/** 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);
/** Names of beans that are currently in creation */
private final Set<String> singletonsCurrentlyInCreation =
            Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16));

功能介紹:

緩存 用途
singletonObjects 用于存放完全初始化好的 bean,從該緩存中取出的 bean 可以直接使用
earlySingletonObjects 存放原始的 bean 對象(尚未填充屬性),用于解決循環(huán)依賴
singletonFactories 存放 bean 工廠對象,用于解決循環(huán)依賴

可以把singletonObjects 看做一級緩存,bean創(chuàng)建時默認(rèn)會先從singletonObjects中取bean,并且在創(chuàng)建后直接將對象放入singletonObjects中緩存;earlySingletonObjects看做二級緩存,在bean創(chuàng)建時提前將ObjectFactory暴露給singletonFactories,這樣就可以在對象創(chuàng)建完成之前也可以獲得對象的引用。

2.2bean創(chuàng)建的過程

實例化流程圖如下:


AbstarctBeanFactorygetBean開始,是個空殼方法,直接調(diào)用doGetBean(name, null, null, false),該方法的分析如下(只分析了比較重要部分的代碼,其他地方有所省略):

protected <T> T doGetBean(
            final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
            throws BeansException {

    final String beanName = transformedBeanName(name);
    Object bean;

    // 在獲取bean的時候優(yōu)先從緩存中獲取,對于新創(chuàng)建的對象,這里是無法獲得值;對于依賴的對象,可能已經(jīng)創(chuàng)建或者正在創(chuàng)建,
    //直接從緩存中獲取,可以避免重復(fù)創(chuàng)建
    Object sharedInstance = getSingleton(beanName);①
    if (sharedInstance != null && args == null) {
        //判斷你是否FactoryBean,如果是FactoryBean類型,則調(diào)用getObject方法注入的實際對象
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }

    else {
        // 對于作用域為prototype的bean,每次請求都會創(chuàng)建一個實例對象,
        // 如果正在創(chuàng)建或已經(jīng)創(chuàng)建,則異常,原因為:對于prototype作用域的bean,
        // Spring不會進(jìn)行緩存,因此無法提前暴露一個創(chuàng)建中的bean
        if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }

        ...

        try {
            final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);

            // 此處用于實例化bean的依賴,比如A依賴B,如果B還未實例化,則先實例化B;
            // 即調(diào)用getBean對B進(jìn)行實例化
            String[] dependsOn = mbd.getDependsOn();
            if (dependsOn != null) {
                for (String dep : dependsOn) {
                    registerDependentBean(dep, beanName);
                    getBean(dep);
                }
            }

            // socpe="singleton"的情況
            if (mbd.isSingleton()) {
                // 此處的關(guān)鍵在于創(chuàng)建的匿名對象ObjectFactory及getObject方法,getObject為實際創(chuàng)建bean實例的地方
                sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
                    @Override
                    public Object getObject() throws BeansException {
                        try {
                            // 創(chuàng)建bean的關(guān)鍵
                            return createBean(beanName, mbd, args);
                        }
                        catch (BeansException ex) {
                            destroySingleton(beanName);
                            throw ex;
                        }
                    }
                });②
                bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            }
            // scope="prototype"的情況
            else if (mbd.isPrototype()) {
                Object prototypeInstance = null;
                try {
                    //創(chuàng)建prototype對象時的前置處理:用于設(shè)置ThreadLocal變量,表示bean正在創(chuàng)建
                    beforePrototypeCreation(beanName);
                    //prototype的核心依然是調(diào)用createBean
                    prototypeInstance = createBean(beanName, mbd, args);
                }
                finally {
                    //創(chuàng)建prototype對象時的后置處理:移除ThreadLocal變量
                    afterPrototypeCreation(beanName);
                }
                bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
            }
            //scope作用域為其他值的情況:比如request、session及globalSession
            else {
                String scopeName = mbd.getScope();
                final Scope scope = this.scopes.get(scopeName);
                if (scope == null) {
                    throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                }
                try {
                    //調(diào)用實際作用域處理方法,此處也設(shè)置了ObjectFactory對象及調(diào)用的createBean方法
                    Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
                        @Override
                        public Object getObject() throws BeansException {
                            beforePrototypeCreation(beanName);
                            try {
                                return createBean(beanName, mbd, args);
                            }
                            finally {
                                afterPrototypeCreation(beanName);
                            }
                        }
                    });
                    bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                }
                catch (IllegalStateException ex) {
                    throw new BeanCreationException(beanName,
                            "Scope '" + scopeName + "' is not active for the current thread; consider " +
                            "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                            ex);
                }
            }
        }
        catch (BeansException ex) {
            cleanupAfterBeanCreationFailure(beanName);
            throw ex;
        }
    }

    return (T) bean;
}

代碼中已經(jīng)標(biāo)識比較重要的部分,現(xiàn)在對其進(jìn)行分析,其中①處:
Object sharedInstance = getSingleton(beanName),具體調(diào)用的方法為:

//用于解決單例循環(huán)依賴的問題,此處allowEarlyReference為true,即允許早期引用(可能對象剛使用構(gòu)造函數(shù)實例化,還未設(shè)置實例的屬性等,屬于未完全實例化的對象,但是持有該對象的引用,等對象完全初始化后可直接使用)
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        //先從singletonObjects中獲取,新創(chuàng)建的此處為空,新建對象在此處返回null后并不會執(zhí)行
        //下面的代碼,會返回,調(diào)用②出的getSingleton(beanName, ObjectFactory)方法;
        //只有在存在被依賴并且該實例正在創(chuàng)建的的情況下才會繼續(xù)執(zhí)行
        Object singletonObject = this.singletonObjects.get(beanName);
        //如果尚未創(chuàng)建并且此對象為正在創(chuàng)建的單例
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
               //從earlySingletonObjects中獲取,ObjectFactory調(diào)用后會將對象放到此處緩存
                singletonObject = this.earlySingletonObjects.get(beanName);
                //此處允許早期引用
                if (singletonObject == null && allowEarlyReference) {
                    //獲取ObjectFactory工廠,使用對應(yīng)的ObjectFactory創(chuàng)建bean
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        //此處調(diào)用注冊的匿名ObjectFactory方法,實際調(diào)用的是getEarlyBeanReference方法(AbstractAutowireCapableBeanFactory的doCreateBean中注冊的,暴露出的實例引用)
                        singletonObject = singletonFactory.getObject();
                        //創(chuàng)建后將此單例設(shè)置到earlySingletonObjects緩存
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return (singletonObject != NULL_OBJECT ? singletonObject : null);
    }

標(biāo)識②中的代碼,即:
getSingleton(String beanName, ObjectFactory<?> singletonFactory)方法,此處對對bean進(jìn)行實例化操作,并將其加入到緩存,代碼如下:

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    //加鎖,避免重復(fù)執(zhí)行創(chuàng)建
    synchronized (this.singletonObjects) {
        //雙重檢查singletonObjects緩存,避免被其他依賴的對象創(chuàng)建完成
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {
             ...
            //前置處理:設(shè)置到singletonsCurrentlyInCreation中,表示此單例正在創(chuàng)建
            beforeSingletonCreation(beanName);
            boolean newSingleton = false;
            ...

            try {
                //調(diào)用getObject方法,實際就是調(diào)用createBean方法
                singletonObject = singletonFactory.getObject();③
                newSingleton = true;
            }
            catch (IllegalStateException ex) {
                throw ex
            }
            catch (BeanCreationException ex) {
                throw ex;
            }
            finally {
                //后置處理:從singletonsCurrentlyInCreation中移除,表示對象創(chuàng)建完畢
                afterSingletonCreation(beanName);
            }
            if (newSingleton) {
                //bean完成實例化將其放入singletonObjects緩存,并清除singletonFactories和earlySingletonObjects
                addSingleton(beanName, singletonObject);
            }
        }
        return (singletonObject != NULL_OBJECT ? singletonObject : null);
    }
}

在創(chuàng)建實例后將其放入緩存中:

protected void addSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
            this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
}

下面分析上面標(biāo)識③的代碼,實際執(zhí)行的就是注冊O(shè)bjectFactory的getObject方法,即createBean方法:

@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
    
    RootBeanDefinition mbdToUse = mbd;
    ....

    try {
        // 此處針對存在targetClass的bean,在實例化之前執(zhí)行注冊InstantiationAwareBeanPostProcessor
        //的postProcessBeforeInstantiation方法,比如執(zhí)行AbstractAutoProxyCreateor創(chuàng)建代理類,此處以后介紹
        Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
        if (bean != null) {
            return bean;
        }
    }
    catch (Throwable ex) {
        throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
                "BeanPostProcessor before instantiation of bean failed", ex);
    }
    //創(chuàng)建bean的實例
    Object beanInstance = doCreateBean(beanName, mbdToUse, args);
    if (logger.isDebugEnabled()) {
        logger.debug("Finished creating instance of bean '" + beanName + "'");
    }
    return beanInstance;
}

最終創(chuàng)建bean實例的地方是doCreateBean,此方法比較復(fù)雜,單最關(guān)鍵的就幾處,分別為:

  • [x] createBeanInstance(beanName, mbd, args)
  • [ ] applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName)
  • [ ] addSingletonFactory(beanName, ObjectFactory)
  • [x] populateBean(beanName, mbd, instanceWrapper)
  • [x] initializeBean(beanName, exposedObject, mbd)
  • [ ] getSingleton(beanName, false)
  • [x] registerDisposableBeanIfNecessary
    其中最重要的為上面重點標(biāo)識的部分
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
            throws BeanCreationException {

    // Instantiate the bean.
    BeanWrapper instanceWrapper = null;
    ...
    if (instanceWrapper == null) {
        //創(chuàng)建bean的實例:實際調(diào)用構(gòu)造方法進(jìn)行反射創(chuàng)建實例
        instanceWrapper = createBeanInstance(beanName, mbd, args);①
    }
    ...
    // Allow post-processors to modify the merged bean definition.
    synchronized (mbd.postProcessingLock) {
        if (!mbd.postProcessed) {
            try {
                //bean合并后的處理,Autowired注解正是通過此方法實現(xiàn)諸如類型的預(yù)解析
                applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);②
            }
            catch (Throwable ex) {
                throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                        "Post-processing of merged bean definition failed", ex);
            }
            mbd.postProcessed = true;
        }
    }

    // 判斷是否需要早期暴露:即是單例、允許循環(huán)依賴并且已經(jīng)正在創(chuàng)建中,
    // 此處設(shè)置bean的ObjectFactory,用于bean的暴露,處理循環(huán)依賴的情況
    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");
        }
        //實例化后就暴露到singletonFactories緩存中
        addSingletonFactory(beanName, new ObjectFactory<Object>() {
            @Override
            public Object getObject() throws BeansException {
                //如果不存在代理,則直接返回,否則調(diào)用SmartInstantiationAwareBeanPostProcessor
                //的getEarlyBeanReference方法,即做增強處理
                return getEarlyBeanReference(beanName, mbd, bean);
            }
        });③
    }

    // Initialize the bean instance.
    Object exposedObject = bean;
    try {
        //處理屬性依賴
        populateBean(beanName, mbd, instanceWrapper);④
        if (exposedObject != null) {
            //執(zhí)行aware、BeanPostProcessor及InitializeBean、init-method處理
            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) {
        //調(diào)用getSingleton方法,此處最多執(zhí)行到earlySingletonObjects緩存中取對象
        Object earlySingletonReference = getSingleton(beanName, false);
        if (earlySingletonReference != null) {
            //如果對bean進(jìn)行了增強處理,此處為false
            if (exposedObject == bean) {
                exposedObject = earlySingletonReference;
            }
            //處理存在依賴此bean的其他bean的情況
            else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                //獲得依賴此bean的bean名稱
                String[] dependentBeans = getDependentBeans(beanName);
                Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length);
                for (String dependentBean : dependentBeans) {
                    //如果dependentBean已經(jīng)實例化,則放入actualDependentBeans中
                    if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                        actualDependentBeans.add(dependentBean);
                    }
                }
                //在此bean實例化完成之前,依賴此bean的其他bean存在已經(jīng)實例化完成的情況,
                //不如何先處理依賴bean,再處理被依賴bean的情況
                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.");
                }
            }
        }
    }

    // 注冊DisposableBean和destory-method,用于bean銷毀時的回調(diào)處理
    try {
        registerDisposableBeanIfNecessary(beanName, bean, mbd);
    }
    catch (BeanDefinitionValidationException ex) {
        throw new BeanCreationException(
                mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
    }

    return exposedObject;
}

綜上所述,對于循環(huán)依賴,比如A依賴B,B依賴A,在A進(jìn)行實例化的時候,即調(diào)用getBean的時候,執(zhí)行完createBeanInstance方法,會將A的實例通過ObjectFactory注冊到singletonFactories中提前暴露,在設(shè)置A的屬性的時候,即執(zhí)行populateBean的時候,發(fā)現(xiàn)依賴的屬性B尚未實例化,則針對B調(diào)用getBean方法,再處理B的實例化時,針對依賴的A會調(diào)用getBean的getSingleton方法,進(jìn)而調(diào)用singletonFactories緩存中注冊O(shè)bjectFactory的getObject方法,即getEarlyBeanReference方法,得到A的實例引用,然后返回處理完成B的實例化,再進(jìn)行A的實例化處理,進(jìn)而處理了循環(huán)依賴的問題。

3、setter的prototype依賴

Spring無法處理prototype的循環(huán)依賴問題,因為無法對prototype作用域的對象進(jìn)行緩存,會報如下異常:

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: 

Spring針對循環(huán)依賴的分析暫時到這里,其中包含了部分對bean實例化過程的分析,接下來實例化bean的時候具體的處理過程,即上述標(biāo)識的三個部分,會在下一章節(jié)進(jìn)行分析。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容