Spring如何解決循環(huán)依賴

1.背景說明

● 循環(huán)依賴是什么?

有一個(gè)Bean為AService,另一個(gè)Bean為BService。

AService里面引用了屬性BService,BService里面又引用了屬性AService,這樣在加載Bean的時(shí)候,造成對(duì)彼此的循環(huán)依賴。

這樣會(huì)導(dǎo)致Bean無法加載。

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

說明:這里只解決單例bean的循環(huán)依賴問題。

核心思路:只需要增加一個(gè) 緩存 來存放 原始對(duì)象 即可,Spring解決此類循環(huán)依賴主要是靠三級(jí)緩存在【屬性注入】階段產(chǎn)生作用。

具體做法:在創(chuàng)建 AService 時(shí),實(shí)例化后將 原始對(duì)象 存放到緩存中(提早暴露),然后依賴注入時(shí)發(fā)現(xiàn)需要 BService,便會(huì)去創(chuàng)建 BService,實(shí)例化后同樣將 原始對(duì)象 存放到緩存中,然后依賴注入時(shí)發(fā)現(xiàn)需要 AService 便會(huì)從緩存中取出并注入,這樣 BService 就完成了創(chuàng)建,隨后 AService 也就能完成屬性注入,最后也完成創(chuàng)建。這樣就打破了環(huán)形調(diào)用,避免循環(huán)依賴問題。

2.三級(jí)緩存

● 一級(jí)緩存:SingletonObjects,緩存的是已經(jīng)實(shí)例化、屬性注入、初始化后的bean對(duì)象。

● 二級(jí)緩存:earlySingletonObjects,緩存的是實(shí)例化后,但未屬性注入、初始化的Bean對(duì)象,用于提前暴露Bean

● 三級(jí)緩存:singletonFactories,緩存的是一個(gè)ObjectFactory,主要作用是生成原始對(duì)象進(jìn)行AOP操作后的代理對(duì)象

3.源碼分析

3.1 入口

AbstractApplicationContext.java的 這個(gè)方法進(jìn)去:

// 初始化 非懶加載的單例 bean

beanFactory.preInstantiateSingletons();

        // Stop using the temporary ClassLoader for type matching.
        beanFactory.setTempClassLoader(null);

        // Allow for caching all bean definition metadata, not expecting further changes.
        beanFactory.freezeConfiguration();

        // Instantiate all remaining (non-lazy-init) singletons.
        beanFactory.preInstantiateSingletons();

進(jìn)入到 DefaultListableBeanFactory的preInstantiateSingletons方法

        // Trigger initialization of all non-lazy singleton beans...
        for (String beanName : beanNames) {
            RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
            if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
                if (isFactoryBean(beanName)) {
                    Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
                    if (bean instanceof FactoryBean) {
                        FactoryBean<?> factory = (FactoryBean<?>) bean;
                        boolean isEagerInit;
                        if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                            isEagerInit = AccessController.doPrivileged(
                                    (PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,
                                    getAccessControlContext());
                        }
                        else {
                            isEagerInit = (factory instanceof SmartFactoryBean &&
                                    ((SmartFactoryBean<?>) factory).isEagerInit());
                        }
                        if (isEagerInit) {
                            getBean(beanName);
                        }
                    }
                }
                else {
                    getBean(beanName);
                }
            }
        }

3.2 getBean方法

DefaultListableBeanFactory中,進(jìn)入getBean

這里獲取的是這種類型的bean:

(1)非抽象的BeanDefinition

(2)單例的BeanDefinition

(3)非懶加載的BeanDefinition

else {
    getBean(beanName);
}

3.3 doGetBean方法

實(shí)際干活的是 AbstractBeanFactory中的 doGetBean

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

如果不存在已有的單例對(duì)象,且beanDefination是單例,那么進(jìn)入 createBean方法

                if (mbd.isSingleton()) {
                    sharedInstance = getSingleton(beanName, () -> {
                        try {
                            return createBean(beanName, mbd, args);
                        }
                        catch (BeansException ex) {
                            // Explicitly remove instance from singleton cache: It might have been put there
                            // eagerly by the creation process, to allow for circular reference resolution.
                            // Also remove any beans that received a temporary reference to the bean.
                            destroySingleton(beanName);
                            throw ex;
                        }
                    });
                    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                }

3.4 createBean方法

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
            throws BeanCreationException

createBean方法 返回一個(gè)Object類型

核心邏輯:

try {
    Object beanInstance = doCreateBean(beanName, mbdToUse, args);
    if (logger.isTraceEnabled()) {
        logger.trace("Finished creating instance of bean '" + beanName + "'");
    }
    return beanInstance;
}

3.5 doCreateBean方法

3.5.1 實(shí)例化對(duì)象

    protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
            throws BeanCreationException

● 首先,實(shí)例化一個(gè)對(duì)象:

instanceWrapper = createBeanInstance(beanName, mbd, args)

3.5.2 加入三級(jí)緩存

接著,把創(chuàng)建對(duì)象的lambda表達(dá)式放到三級(jí)緩存。

這里是為了避免后期的循環(huán)依賴,所以在bean初始化完成前將創(chuàng)建實(shí)例的ObjectFactory加入工廠。

boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
 isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
   if (logger.isTraceEnabled()) {
   logger.trace("Eagerly caching bean '" + beanName +
   "' to allow for resolving potential circular references");
}
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

說明:

(1)getEarlyBeanReference(beanName, mbd, bean) 方法 會(huì)返回遍歷工廠內(nèi)的所有后置處理器,去處理原始的bean,并返回最終經(jīng)過層層包裝后的 代理對(duì)象

(2)這里并不會(huì)馬上執(zhí)行g(shù)etEarlyBeanReference方法,有循環(huán)依賴的時(shí)候才執(zhí)行。

3.5.3 屬性賦值

場(chǎng)景:AService 中要注入屬性BService

進(jìn)入populateBean方法

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) 

核心方法:

if (pvs != null) {
 applyPropertyValues(beanName, mbd, bw, pvs);
}

applyPropertyValues中的核心方法:

Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);

點(diǎn)進(jìn)去:

if (value instanceof RuntimeBeanReference) {
   RuntimeBeanReference ref = (RuntimeBeanReference) value;
   return resolveReference(argName, ref);
}

核心方法:resolveReference(argName, ref),點(diǎn)進(jìn)去:

else {
    String resolvedName;
    if (beanType != null) {
      NamedBeanHolder<?> namedBean = 
      this.beanFactory.resolveNamedBean(beanType);
      bean = namedBean.getBeanInstance();
      resolvedName = namedBean.getBeanName();
     } else {
        resolvedName = String.valueOf(doEvaluate(ref.getBeanName()));
        bean = this.beanFactory.getBean(resolvedName);
     }
     this.beanFactory.registerDependentBean(resolvedName, this.beanName);
}

注意:這里開始無限套娃之旅,即:

(1)這里有一句:bean = this.beanFactory.getBean(resolvedName),在該場(chǎng)景下,作用是獲取屬性Bservice。

(2)然后 B在創(chuàng)建過程中,最終又會(huì)執(zhí)行這句bean = this.beanFactory.getBean(resolvedName),去獲取屬性Aservice

3.6 循環(huán)依賴的閉環(huán)

● 閉環(huán)形成的關(guān)鍵點(diǎn)

當(dāng) Aservice --> Bservice --> Aservice 這個(gè)過程,進(jìn)行到第二次 獲取 Aservice 的時(shí)候,會(huì)通過 getSingleton 方法

獲取Aservice,即:

可以看到在第三級(jí)緩存中調(diào)用了 singletonFactories.get(beanName)。

按照上文所說,會(huì)觸發(fā)執(zhí)行有 AOP 操作,返回代理對(duì)象。如果沒有AOP操作的話,返回原始對(duì)象。

此外,還會(huì)把Aservice上移到二級(jí)緩存中、并刪除三級(jí)緩存的數(shù)據(jù)。

● Bservice創(chuàng)建完成

如此一來,容器在創(chuàng)建Bservice的時(shí)候,就可以拿到 Aservice,完成BService的創(chuàng)建,并將Bservice加入一級(jí)緩存。

● Aservice創(chuàng)建完成

當(dāng)Bservice創(chuàng)建完成后,就可以在Aservice中完成屬性Bservice的注入,從而完成AService的創(chuàng)建,將Aservice加入一級(jí)緩存。

4.總結(jié)

梳理整個(gè)過程如下:

● 首先會(huì)獲取 AService 對(duì)應(yīng)的 Bean 對(duì)象。

先是調(diào)用 doGetBean() 中的第一個(gè) getSingleton(beanName) 判斷是否有該 Bean 的實(shí)例,有就直接返回了。(顯然這里沒有)

然后調(diào)用 doGetBean() 中的第二個(gè) getSingleton() 方法來執(zhí)行 doCreateBean() 方法。

先進(jìn)行實(shí)例化操作(也就是利用構(gòu)造函數(shù)實(shí)例化),此時(shí)實(shí)例化后生成的是原始對(duì)象。

將原始對(duì)象通過 lambda表達(dá)式 進(jìn)行封裝成 ObjectFactory 對(duì)象,通過 addSingletonFactory 加入三級(jí)緩存中。

然后再進(jìn)行屬性注入,此時(shí)發(fā)現(xiàn)需要注入 BService 的 Bean,會(huì)通過 doGetBean() 去獲取 BService 對(duì)應(yīng)的 Bean。

● 獲取BService 對(duì)應(yīng)的 Bean。

同樣調(diào)用 doGetBean() 中的第一個(gè) getSingleton(beanName) 判斷是否有該 Bean 的實(shí)例,顯然這里也是不會(huì)有 BService 的 Bean 的。

然后只能調(diào)用 doGetBean() 中的第二個(gè) getSingleton() 方法來執(zhí)行 doCreateBean() 方法來創(chuàng)建一個(gè) BService 的 Bean。

同樣地先進(jìn)行實(shí)例化操作,生成原始對(duì)象后封裝成 ObjectFactory 對(duì)象放入三級(jí)緩存中。

然后進(jìn)行屬性注入,此時(shí)發(fā)現(xiàn)需要注入 AService 的 Bean。

● 第二次獲取AService 對(duì)應(yīng)的 Bean

此時(shí)調(diào)用調(diào)用 doGetBean() 中的第一個(gè) getSingleton(beanName) 查找是否有 AService 的 Bean。此時(shí)會(huì)觸發(fā)三級(jí)緩存,也就是調(diào)用 singletonFactories.get(beanName)。

因?yàn)槿?jí)緩存中有 AService 的原始對(duì)象封裝的 ObjectFactory 對(duì)象,所以可以獲取到的代理對(duì)象或原始對(duì)象,并且上移到二級(jí)緩存中,提前暴露給 BService 調(diào)用。

所以 BService 可以完成屬性注入,然后進(jìn)行初始化后,將 Bean 放入一級(jí)緩存,這樣 AService 也可以完成創(chuàng)建。

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

相關(guān)閱讀更多精彩內(nèi)容

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