【啃啊啃 Spring5 源碼】細(xì)碎二:bean的循環(huán)依賴

循環(huán)依賴

所謂循環(huán)依賴,是指在spring中,類A中有一個(gè)成員變量為類B,而類B中也有一個(gè)成員變量為類A。這會(huì)造成在spring初始化容器中的bean時(shí),bean A的初始化需要bean B的初始化的完成,而bean B的初始化又需要bean A初始化的完成。

A與B間形成了循環(huán)依賴:


循環(huán)依賴的解決

spring中,循環(huán)依賴的解決是有條件的:僅限于singleton(單例)作用域的bean,且依賴的成員變量不是在構(gòu)造函數(shù)中初始化的

不能解決的情況一:

原型prototype作用域的bean發(fā)生循環(huán)依賴會(huì)直接拋出異常。

/**AbstractBeanFactory.doGetBean()方法*/
// 如果原型作用域的bean在獲取時(shí),發(fā)現(xiàn)正在創(chuàng)建中
// 即發(fā)生了循環(huán)依賴,直接拋出異常
if (isPrototypeCurrentlyInCreation(beanName)) {
        throw new BeanCurrentlyInCreationException(beanName);
}

這種情況好理解,原型的bean如果允許循環(huán)依賴,那一定會(huì)處在不斷創(chuàng)建依賴類的循環(huán)中,最終導(dǎo)致堆溢出

不能解決的情況二:

單例bean,但依賴的成員變量在構(gòu)造函數(shù)中初始化。如:

public class A {
    private B b;

    public A(B b) {
        this.b = b;
    }
}

public class B {
    @Autowired
    private A a;
}

//配置文件:
<bean id="a" class="test.spring.bean.A" >
    <constructor-arg ref="b" index="0" />
</bean>
<bean id="b" class="test.spring.bean.B" />

簡(jiǎn)單說(shuō)明一下原因,spring中的循環(huán)依賴的解決關(guān)鍵在于:
????緩存 實(shí)例化成功,但還沒(méi)注入成員變量的bean,等發(fā)生循環(huán)依賴時(shí),直接引用緩存中只實(shí)例化過(guò)的bean,這樣就不用等待依賴bean繼續(xù)注入成員變量,然后發(fā)生循環(huán)依賴。

如果實(shí)例化過(guò)程中就發(fā)生了循環(huán)依賴,還沒(méi)來(lái)得及緩存,那就只有拋出異常了。

循環(huán)依賴的解決:

當(dāng)我們調(diào)用context.getBean()方法來(lái)獲取一個(gè)單例bean時(shí),如果這個(gè)bean在容器中還沒(méi)有,會(huì)進(jìn)行創(chuàng)建(AbstractAutowireCapableBeanFactory.createBean())。

創(chuàng)建這個(gè)bean的過(guò)程簡(jiǎn)單可分為兩步:實(shí)例化(instantiate,使用構(gòu)造函數(shù)創(chuàng)建bean對(duì)象)和初始化(initialize,注入bean的成員變量):

1. 存入map緩存

當(dāng)實(shí)例化完后,會(huì)將構(gòu)造好的bean對(duì)象存在singletonFactories緩存map中,見(jiàn)AbstractAutowireCapableBeanFactory.doCreateBean()

          // 如果bean為單例且允許循環(huán)依賴,將該bean的singletonFactory存在map中暴露
        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");
            }
            //重點(diǎn)!!!
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        }

addSingletonFactory()方法中,beanName-ObjectFactory這樣的key-value被放進(jìn)了singletonFactoriesmap緩存集合。

這里注意,實(shí)例化的bean并不是直接存入緩存map,而是通過(guò)ObjectFactory工廠對(duì)象被暴露在了緩存里,后面循環(huán)依賴時(shí),也是以ObjectFactory形式被取出。然后調(diào)用ObjectFactory.getObject()——》getEarlyBeanReference()方法,來(lái)獲取到被SmartInstantiationAwareBeanPostProcessor后處理器處理過(guò)的剛剛實(shí)例化結(jié)束的bean對(duì)象。

這里的ObjectFactory發(fā)生過(guò)替換。bean在第一次獲取時(shí),生成的ObjectFactory對(duì)象,getObject()方法實(shí)際上會(huì)調(diào)用createBean()方法創(chuàng)建bean。而bean在創(chuàng)建的過(guò)程中,實(shí)例化完成后會(huì)重新創(chuàng)建ObjectFactory對(duì)象,getObject()方法會(huì)變成調(diào)用getEarlyBeanReference(),用于獲取的緩存的實(shí)例化的bean。

2. 循環(huán)依賴時(shí),從緩存中取出

當(dāng)實(shí)例化bean A存入緩存后,我們初始化A ,這時(shí)會(huì)注入依賴類B(getBean()方法)。

依賴類B注入時(shí)進(jìn)行創(chuàng)建,實(shí)例化——》初始化時(shí),又循環(huán)依賴了bean A,但這時(shí),緩存中已經(jīng)保存了實(shí)例化的bean A,我們通過(guò)getSingleton()方法直接獲取A對(duì)象注入后返回。循環(huán)依賴被破解。

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

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

        // 從緩存中獲取單例bean,緩存中的bean只被實(shí)例化,還未初始化
        Object sharedInstance = getSingleton(beanName);
        //獲取成功,直接返回bean
        if (sharedInstance != null && args == null) {
                  …………
        }    
                ………………
}

//從singletonFactories中獲取該beanName的ObjectFactory,然后通過(guò)ObjectFactory獲取bean
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
}

3. 整個(gè)流程

最后梳理下整個(gè)循環(huán)依賴解決的流程,以上面的A、B為例:

  1. context.getBean(A.class)獲取容器中的單例A,此時(shí)bean A對(duì)象還不存在,進(jìn)行創(chuàng)建。
  2. 實(shí)例化bean A,將其放入緩存
  3. 初始化A,注入依賴類B
  4. 通過(guò)getBean(B.class)獲取依賴類B,發(fā)現(xiàn)bean B在容器中不存在,進(jìn)行創(chuàng)建
  5. 實(shí)例化B,將其放入緩存
  6. 初始化B,注入依賴類A
  7. 通過(guò)getBean(A.class)獲取依賴類A,此時(shí)發(fā)現(xiàn)緩存中已經(jīng)存在實(shí)例化的A,獲取后返回
  8. B初始化成功,返回
  9. A初始化成功, 返回bean A

注意:整個(gè)流程結(jié)束后,B持有的也不再是實(shí)例化的A,而是初始化結(jié)束的A。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容