最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring通過三級(jí)緩存解決循環(huán)依賴問題的過程詳解

 更新時(shí)間:2023年10月27日 09:03:39   作者:韻秋梧桐  
循環(huán)依賴指的是在對(duì)象之間存在相互依賴關(guān)系,形成一個(gè)閉環(huán),導(dǎo)致無法準(zhǔn)確地完成對(duì)象的創(chuàng)建和初始化,本文主要介紹了Spring通過三級(jí)緩存解決循環(huán)依賴的方法,需要的可以參考下

1. 三級(jí)緩存解決的問題場景

循環(huán)依賴指的是在對(duì)象之間存在相互依賴關(guān)系,形成一個(gè)閉環(huán),導(dǎo)致無法準(zhǔn)確地完成對(duì)象的創(chuàng)建和初始化;當(dāng)兩個(gè)或多個(gè)對(duì)象彼此之間相互引用,而這種相互引用形成一個(gè)循環(huán)時(shí),就可能出現(xiàn)循環(huán)依賴問題。

在早期的 Spring 版本中是可以自動(dòng)解決的循環(huán)依賴的問題的,

public class A {
	@Autowired
    private B b;
}

public class B {
	@Autowired
    private A a;
}

但要注意,Spring 解決循環(huán)依賴是有前提條件的,

第一,要求互相依賴的 Bean 必須要是單例的 Bean。

這是因?yàn)閷?duì)于原型范圍的 Bean(prototype scope),每次請(qǐng)求都會(huì)創(chuàng)建一個(gè)新的 Bean 實(shí)例,這樣每次嘗試解析循環(huán)依賴時(shí),都會(huì)產(chǎn)生新的 Bean 實(shí)例,導(dǎo)致無限循環(huán),由于沒有全局的、持續(xù)的單例實(shí)例的緩存來引用,因此循環(huán)依賴無法得到解決。

第二,依賴注入的方式不能都是構(gòu)造函數(shù)注入的方式。

當(dāng)使用構(gòu)造函數(shù)注入時(shí),一個(gè) Bean 的實(shí)例在構(gòu)造函數(shù)被完全調(diào)用之前是不會(huì)被創(chuàng)建的;如果 Bean A 的構(gòu)造函數(shù)依賴于 Bean B,而 Bean B 的構(gòu)造函數(shù)又依賴于 Bean A,那么就會(huì)產(chǎn)生一個(gè)死鎖的情況,因?yàn)閮烧叨疾荒茉趯?duì)方初始化之前完成初始化。

public class C {
    private D d;
    @Autowired
    public C(D d) {
        this.dService = dService;
    }
}

public class D {
    private C c;
    @Autowired
    public D(C c) {
        this.c = c;
    }
}

Spring 源碼中關(guān)于三級(jí)緩存的定義如下:

// 一級(jí)緩存
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 二級(jí)緩存
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
// 三級(jí)緩存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

所以說,所謂的“三級(jí)緩存”就是是指三個(gè) Map 數(shù)據(jù)結(jié)構(gòu),分別用于存儲(chǔ)不同狀態(tài)的 Bean。

2. 三級(jí)緩存的差異性

一級(jí)緩存:

一級(jí)緩存保存的是已經(jīng)完全初始化和實(shí)例化的 Bean 對(duì)象,在程序中使用的 Bean 通常就是從這個(gè)緩存中獲取的;這個(gè)緩存的目的是確保 Bean 只初始化一次(是單例的),避免多次實(shí)例化相同的Bean對(duì)象,提高性能。

二級(jí)緩存:

二級(jí)緩存用來解決 Bean 創(chuàng)建過程中的循環(huán)依賴問題,它存儲(chǔ)的是尚未完成屬性注入和初始化的“半成品”Bean 對(duì)象;當(dāng) Spring容器發(fā)現(xiàn)兩個(gè)或多個(gè) Bean 之間存在循環(huán)依賴時(shí),也就是當(dāng)一個(gè) Bean 創(chuàng)建過程中需要引用另一個(gè)正在創(chuàng)建的 Bean,Spring 將創(chuàng)建需要的這些未完全初始化的對(duì)象提前暴露在二級(jí)緩存中,以便其他 Bean 進(jìn)行引用,確保它們之間的依賴能夠被滿足。

三級(jí)緩存:

三級(jí)緩存中存儲(chǔ)的是 ObjectFactory<?> 類型的代理工廠對(duì)象,主要用于處理存在 AOP 時(shí)的循環(huán)依賴問題;每個(gè) Bean 都對(duì)應(yīng)一個(gè) ObjectFactory 對(duì)象,通過調(diào)用該對(duì)象的 getObject 方法,可以獲取到早期暴露出去的 Bean;在該 Bean 要被其他 Bean 引用時(shí),Spring 就會(huì)用工廠對(duì)象創(chuàng)建出該 Bean 的實(shí)例對(duì)象,最終當(dāng)該 Bean 完成構(gòu)造的所有步驟后就會(huì)將該 Bean 放入到一級(jí)緩存中。

3. 循環(huán)依賴時(shí)的處理流程

當(dāng) Spring 發(fā)生循環(huán)依賴時(shí)(以最開始介紹的場景為例,A B 兩個(gè) Bean 相互依賴),以下是完善的執(zhí)行流程:

  • 遍歷待創(chuàng)建的所有 beanName:在容器啟動(dòng)時(shí),Spring 會(huì)遍歷所有需要?jiǎng)?chuàng)建的 Bean 名稱,在第一次遍歷到 A 時(shí),就開始獲取 Bean A;如果 Bean A 的實(shí)例不在一級(jí)、二級(jí)緩存中(緩存中沒有值),Spring 會(huì)開始正常的 Bean 創(chuàng)建流程。
  • Bean A 的創(chuàng)建:Bean A 的創(chuàng)建過程開始,然后 Spring 會(huì)檢查是否 A 是單例(Singleton),同時(shí) A 是否已經(jīng)創(chuàng)建完成;如果 A 是單例且尚未創(chuàng)建完成,將 A 的 BeanFactory 存入三級(jí)緩存。
  • 處理依賴注入:A 開始處理 @Autowired 注解,嘗試注入 B 屬性;Spring 會(huì)在一級(jí)、二級(jí)緩存中來查找 Bean B,如果找不到,則開始正常創(chuàng)建 Bean B 的流程。
  • Bean B 的創(chuàng)建:Bean B 的創(chuàng)建過程類似于 A,首先判斷B是否是單例,且是否已創(chuàng)建完成,如果否,B 的BeanFactory 也會(huì)被存入三級(jí)緩存。
  • B 注入 A 屬性:B 開始注入 A 屬性,嘗試從一級(jí)、二級(jí)緩存中查找 A;如果在緩存中找不到 A,B 會(huì)嘗試從三級(jí)緩存獲取 A 的 BeanFactory,并通過 BeanFactory的getObject()方法獲取 A 的屬性,此時(shí),A 被存入二級(jí)緩存,同時(shí)清除三級(jí)緩存;因此,B 能夠成功注入 A 屬性,B 接著執(zhí)行初始化,處于實(shí)例化和初始化都已完成的完全狀態(tài)。
  • B 存入一級(jí)緩存:B執(zhí)行addSingleton(),將完全狀態(tài)的 B 存入一級(jí)緩存,并清空二級(jí),三級(jí)緩存。
  • A 繼續(xù)注入 B 屬性:A 繼續(xù)注入 B 屬性,調(diào)用beanFactory.getBean()方法獲取 B,由于第六步已經(jīng)將 B 存入一級(jí)緩存,A 可以直接獲取 B,成功注入 B 屬性, A 接著執(zhí)行初始化,得到一個(gè)完全狀態(tài)的 A。
  • A 存入一級(jí)緩存:A 執(zhí)行addSingleton(),將完全狀態(tài)的 A 存入一級(jí)緩存,并清空二級(jí)緩存。

此時(shí),A 和 B 都被實(shí)例化和初始化完成,解決了循環(huán)依賴的問題;這個(gè)流程確保了每個(gè)Bean在需要時(shí)都能夠獲取到已完全初始化的依賴項(xiàng)。

常見疑問解答

問題一:為什么在 Bean B 被注入 Bean A 之前,需要將 Bean A 存入二級(jí)緩存?

主要原因是,如果存在其他循環(huán)依賴需要用到 A,從二級(jí)緩存中直接取出早期的 Bean A 對(duì)象會(huì)更加高效。

問題二:為什么創(chuàng)建完 Bean 后要清空二、三級(jí)緩存?

清空是為了節(jié)省存儲(chǔ)空間,一旦 Bean 完全初始化并存儲(chǔ)在一級(jí)緩存中,其在二、三級(jí)緩存中的記錄就不再需要了。

問題三:三級(jí)緩存為什么不能解決構(gòu)造器引起的循環(huán)依賴?

這是因?yàn)闃?gòu)造器引起的循環(huán)依賴發(fā)生在 Bean 的實(shí)例化階段,這個(gè)階段比二、三級(jí)緩存處理的階段還要早,無法創(chuàng)建出早期的半成品對(duì)象。

問題四:如果不使用三級(jí)緩存,只使用二級(jí)緩存,能否解決循環(huán)依賴?

肯定是不能的,二級(jí)緩存存儲(chǔ)的 Bean 可能是兩種類型,一種是實(shí)例化階段創(chuàng)建出來的對(duì)象,另一種是實(shí)例化階段創(chuàng)建出來的對(duì)象的代理對(duì)象;是否需要代理對(duì)象取決于你的配置需要,如是否添加了事務(wù)注解或自定義 AOP 切面;如果放棄使用三級(jí)緩存,即沒有 ObjectFactory,那么就需要將早期的 Bean 放入二級(jí)緩存;但問題是,應(yīng)該將未被代理的 Bean 還是代理的 Bean 放入二級(jí)緩存,這只能在屬性注入階段,處理注解時(shí)才能分辨。

  • 如果直接往二級(jí)緩存添加沒有被代理的 Bean,那么此時(shí)注入給其它對(duì)象的 Bean 可能跟最后完全生成的 Bean 是不一樣的,因?yàn)樽詈笊墒褂玫氖强赡艽韺?duì)象,此時(shí)注入的是原始對(duì)象,這這種情況是不允許發(fā)生的。
  • 如果直接往二級(jí)緩存添加一個(gè)代理 Bean,在不確定是否要使用代理對(duì)象的情況下,就有提前暴露代理對(duì)象的可能;正常的代理的對(duì)象都是初始化后期調(diào)用生成的,是基于后置處理器的,若提早的代理就違背了 Bean 定義的生命周期。

Spring 在一個(gè)三級(jí)緩存放置一個(gè)工廠,如果產(chǎn)生循環(huán)依賴 ,這個(gè)工廠的作用就是判斷這個(gè)對(duì)象是否需要代理,如果否則直接返回,如果是則返回代理對(duì)像。

4. 源碼驗(yàn)證

在項(xiàng)目中雙擊 Shift,全局查找文件:AbstractAutowireCapableBeanFactory,找到 550 行左右的 doCreateBean 方法,重點(diǎn)看一下 580 行到 600 行這20行代碼就行,包含了三級(jí)緩存、屬性注入、初始化,精華都在這20行,下面在源碼中給出了關(guān)鍵注釋。

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

    // Instantiate the bean.
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
        instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    // 通過BeanDefinition實(shí)例化對(duì)象
    if (instanceWrapper == null) {
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    Object bean = instanceWrapper.getWrappedInstance();
    Class<?> beanType = instanceWrapper.getWrappedClass();
    if (beanType != NullBean.class) {
        mbd.resolvedTargetType = beanType;
    }

    // Allow post-processors to modify the merged bean definition.
    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.
    // 對(duì)象是否單例、是否未創(chuàng)建完成
    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");
        }
        // 將對(duì)象的工廠加入到三級(jí)緩存
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }

    // Initialize the bean instance.
    Object exposedObject = bean;
    try {
        // 屬性注入 (在這里解析@Autowired注解時(shí),觸發(fā)循環(huán)依賴)
        populateBean(beanName, mbd, instanceWrapper);
        // 初始化
        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) {
            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 " +
                            "'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
                }
            }
        }
    }

    // Register bean as disposable.
    try {
        registerDisposableBeanIfNecessary(beanName, bean, mbd);
    }
    catch (BeanDefinitionValidationException ex) {
        throw new BeanCreationException(
                mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
    }

    return exposedObject;
}

從緩存中獲取 Bean 的源碼

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // Quick check for existing instance without full singleton lock
    Object singletonObject = this.singletonObjects.get(beanName);
    // 從一級(jí)緩存中獲取
    // 如果一級(jí)緩存里沒有,且 Bean 正在創(chuàng)建中
    // 就從二級(jí)緩存里獲取
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        singletonObject = this.earlySingletonObjects.get(beanName);
        // 二級(jí)緩存沒有,就從三級(jí)緩存獲取一個(gè)工廠
        if (singletonObject == null && allowEarlyReference) {
            synchronized (this.singletonObjects) {
                // Consistent creation of early reference within full sin
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    singletonObject = this.earlySingletonObjects.get(beanName);
                    if (singletonObject == null) {
                        ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                        if (singletonFactory != null) {
                            // 能獲取到工廠則創(chuàng)建 Bean
                            singletonObject = singletonFactory.getObject();
                            // 把實(shí)例存入二級(jí)緩存
                            this.earlySingletonObjects.put(beanName, singletonObject);
                            // 把工廠從三級(jí)緩存移除
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
        }
    }
    return singletonObject;
}

總之,Spring 的三級(jí)緩存機(jī)制是一個(gè)巧妙的設(shè)計(jì),它解決了在 Bean 初始化過程中可能出現(xiàn)的循環(huán)依賴問題;對(duì)于 Spring 的用戶來說,這意味著更加穩(wěn)定和可靠的 Bean 管理和依賴注入機(jī)制。

以上就是Spring通過三級(jí)緩存解決循環(huán)依賴問題的過程詳解的詳細(xì)內(nèi)容,更多關(guān)于Spring三級(jí)緩存解決循環(huán)依賴的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 簡單實(shí)現(xiàn)Java版學(xué)生管理系統(tǒng)

    簡單實(shí)現(xiàn)Java版學(xué)生管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了簡單實(shí)現(xiàn)Java版學(xué)生管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-06-06
  • 在SpringBoot項(xiàng)目中獲取Request的四種方法

    在SpringBoot項(xiàng)目中獲取Request的四種方法

    這篇文章主要為大家詳細(xì)介紹了SpringBoot項(xiàng)目中獲取Request的四種方法,文中的示例代碼講解詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴可以學(xué)習(xí)一下
    2023-11-11
  • SpringBoot讀寫操作yml配置文件方法

    SpringBoot讀寫操作yml配置文件方法

    之前一直用的application.properties配置文件,只能是KV結(jié)構(gòu),后來的yml配置文件更像是樹狀結(jié)構(gòu),支持層級(jí),比properties更靈活
    2023-01-01
  • Java Mybatis批量修改封裝詳解

    Java Mybatis批量修改封裝詳解

    這篇文章主要介紹了Mybatis批量修改封裝的相關(guān)內(nèi)容,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • SpringBoot中的Bean裝配詳解

    SpringBoot中的Bean裝配詳解

    Spring?IoC?容器是一個(gè)管理?Bean?的容器,在?Spring?的定義中,它要求所有的?IoC?容器都需要實(shí)現(xiàn)接口?BeanFactory,它是一個(gè)頂級(jí)容器接口,這篇文章主要介紹了SpringBoot中的Bean裝配詳解,需要的朋友可以參考下
    2024-04-04
  • Java集合List的使用詳細(xì)解析

    Java集合List的使用詳細(xì)解析

    這篇文章主要介紹了Java集合List的使用詳細(xì)解析,List集合類中元素有序、且可重復(fù),集合中的每個(gè)元素都有其對(duì)應(yīng)的順序索引,鑒于Java中數(shù)組用來存儲(chǔ)數(shù)據(jù)的局限性,我們通常使用java.util.List替代數(shù)組,需要的朋友可以參考下
    2023-11-11
  • 在es中查詢null值的操作方法

    在es中查詢null值的操作方法

    在我們向es中寫入數(shù)據(jù)時(shí),有些時(shí)候數(shù)據(jù)寫入到es中的是null,或者沒有寫入這個(gè)字段,那么這個(gè)時(shí)候在es中該如何查詢出這種為null的數(shù)據(jù)呢,本文給大家詳細(xì)講解,需要的朋友參考下吧
    2023-02-02
  • java中File類的構(gòu)造函數(shù)及其方法

    java中File類的構(gòu)造函數(shù)及其方法

    這篇文章主要介紹了java中File類的構(gòu)造函數(shù)及其方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-06-06
  • MultipartFile文件判斷是否存在的操作

    MultipartFile文件判斷是否存在的操作

    這篇文章主要介紹了MultipartFile文件判斷是否存在的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • IDEA編寫SpringBoot項(xiàng)目時(shí)使用Lombok報(bào)錯(cuò)“找不到符號(hào)”的原因和解決

    IDEA編寫SpringBoot項(xiàng)目時(shí)使用Lombok報(bào)錯(cuò)“找不到符號(hào)”的原因和解決

    本文主要介紹了IDEA編寫SpringBoot項(xiàng)目時(shí)使用Lombok報(bào)錯(cuò)“找不到符號(hào)”,詳細(xì)介紹了幾種可能會(huì)出現(xiàn)的問題及其解決方法,具有一定的參考價(jià)值,感興趣的可以了解一下
    2025-03-03

最新評(píng)論

北碚区| 民乐县| 宝坻区| 兖州市| 隆安县| 建阳市| 宝丰县| 舒城县| 临泉县| 罗田县| 扶余县| 凤阳县| 贺州市| 田东县| 丰台区| 东港市| 涡阳县| 瑞昌市| 宜城市| 嘉荫县| 广元市| 凤城市| 鄯善县| 贵港市| 犍为县| 阿合奇县| 栖霞市| 东港市| 开远市| 县级市| 集贤县| 敦化市| 新晃| 兴国县| 开远市| 乡宁县| 闻喜县| 沙坪坝区| 清水河县| 信阳市| 冀州市|