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

Spring三級緩存思想解決循環(huán)依賴總結(jié)分析

 更新時(shí)間:2023年08月29日 11:09:46   作者:jacheut  
這篇文章主要為大家介紹了Spring三級緩存思想解決循環(huán)依賴總結(jié)分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

三級緩存思想

Spring 解決循環(huán)依賴的核心就是提前暴露對象,而提前暴露的對象就是放置于第二級緩存中。下表是三級緩存的說明:

所有被 Spring 管理的 Bean,最終都會存放在 singletonObjects 中,這里面存放的 Bean 是經(jīng)歷了所有生命周期的(除了銷毀的生命周期),完整的,可以給用戶使用的。

earlySingletonObjects 存放的是已經(jīng)被實(shí)例化,但是還沒有注入屬性和執(zhí)行 init 方法的 Bean。

singletonFactories 存放的是生產(chǎn) Bean 的工廠。

解決循環(huán)依賴

Spring 是如何通過上面介紹的三級緩存來解決循環(huán)依賴的呢?這里只用 A,B 形成的循環(huán)依賴來舉例:

  • 實(shí)例化 A,此時(shí) A 還未完成屬性填充和初始化方法(@PostConstruct)的執(zhí)行,A 只是一個(gè)半成品。
  • 為 A 創(chuàng)建一個(gè) Bean 工廠,并放入到 singletonFactories 中。
  • 發(fā)現(xiàn) A 需要注入 B 對象,但是一級、二級、三級緩存均為發(fā)現(xiàn)對象 B。
  • 實(shí)例化 B,此時(shí) B 還未完成屬性填充和初始化方法(@PostConstruct)的執(zhí)行,B 只是一個(gè)半成品。
  • 為 B 創(chuàng)建一個(gè) Bean 工廠,并放入到 singletonFactories 中。
  • 發(fā)現(xiàn) B 需要注入 A 對象,此時(shí)在一級、二級未發(fā)現(xiàn)對象 A,但是在三級緩存中發(fā)現(xiàn)了對象 A,從三級緩存中得到對象 A,并將對象 A 放入二級緩存中,同時(shí)刪除三級緩存中的對象 A。(注意,此時(shí)的 A 還是一個(gè)半成品,并沒有完成屬性填充和執(zhí)行初始化方法)
  • 將對象 A 注入到對象 B 中。
  • 對象 B 完成屬性填充,執(zhí)行初始化方法,并放入到一級緩存中,同時(shí)刪除二級緩存中的對象 B。(此時(shí)對象 B 已經(jīng)是一個(gè)成品)
  • 對象 A 得到對象 B,將對象 B 注入到對象 A 中。(對象 A 得到的是一個(gè)完整的對象 B)
  • 對象 A 完成屬性填充,執(zhí)行初始化方法,并放入到一級緩存中,同時(shí)刪除二級緩存中的對象 A。

我們從源碼中來分析整個(gè)過程:

創(chuàng)建 Bean 的方法在 AbstractAutowireCapableBeanFactory::doCreateBean()

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
    BeanWrapper instanceWrapper = null;
    if (instanceWrapper == null) {
        // ① 實(shí)例化對象
        instanceWrapper = this.createBeanInstance(beanName, mbd, args);
    }
    final Object bean = instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null;
    Class<?> beanType = instanceWrapper != null ? instanceWrapper.getWrappedClass() : null;
    // ② 判斷是否允許提前暴露對象,如果允許,則直接添加一個(gè) ObjectFactory 到三級緩存
    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                isSingletonCurrentlyInCreation(beanName));
    if (earlySingletonExposure) {
        // 添加三級緩存的方法詳情在下方
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }
    // ③ 填充屬性
    this.populateBean(beanName, mbd, instanceWrapper);
    // ④ 執(zhí)行初始化方法,并創(chuàng)建代理
    exposedObject = initializeBean(beanName, exposedObject, mbd);
    return exposedObject;
}

添加三級緩存的方法如下:

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
        if (!this.singletonObjects.containsKey(beanName)) { // 判斷一級緩存中不存在此對象
            this.singletonFactories.put(beanName, singletonFactory); // 添加至三級緩存
            this.earlySingletonObjects.remove(beanName); // 確保二級緩存沒有此對象
            this.registeredSingletons.add(beanName);
        }
    }
}
@FunctionalInterface
public interface ObjectFactory<T> {
    T getObject() throws BeansException;
}

通過這段代碼,我們可以知道 Spring 在實(shí)例化對象之后,就會為其創(chuàng)建一個(gè) Bean 工廠,并將此工廠加入到三級緩存中。

因此,Spring 一開始提前暴露的并不是實(shí)例化的 Bean,而是將 Bean 包裝起來的 ObjectFactory。為什么要這么做呢?

這實(shí)際上涉及到 AOP,如果創(chuàng)建的 Bean 是有代理的,那么注入的就應(yīng)該是代理 Bean,而不是原始的 Bean。但是 Spring 一開始并不知道 Bean 是否會有循環(huán)依賴,通常情況下(沒有循環(huán)依賴的情況下),Spring 都會在完成填充屬性,并且執(zhí)行完初始化方法之后再為其創(chuàng)建代理。但是,如果出現(xiàn)了循環(huán)依賴的話,Spring 就不得不為其提前創(chuàng)建代理對象,否則注入的就是一個(gè)原始對象,而不是代理對象。因此,這里就涉及到應(yīng)該在哪里提前創(chuàng)建代理對象?

Spring 的做法就是在 ObjectFactory 中去提前創(chuàng)建代理對象。它會執(zhí)行 getObject() 方法來獲取到 Bean。實(shí)際上,它真正執(zhí)行的方法如下:

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                // 如果需要代理,這里會返回代理對象;否則返回原始對象
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
}

因?yàn)樘崆斑M(jìn)行了代理,避免對后面重復(fù)創(chuàng)建代理對象,會在 earlyProxyReferences 中記錄已被代理的對象。

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
        implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        // 記錄已被代理的對象
        this.earlyProxyReferences.put(cacheKey, bean);
        return wrapIfNecessary(bean, beanName, cacheKey);
    }
}

通過上面的解析,我們可以知道 Spring 需要三級緩存的目的是為了在沒有循環(huán)依賴的情況下,延遲代理對象的創(chuàng)建,使 Bean 的創(chuàng)建符合 Spring 的設(shè)計(jì)原則。

如何獲取依賴

通過一個(gè) getSingleton() 方法去獲取所需要的 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) {
                    // Bean 工廠中獲取 Bean
                    singletonObject = singletonFactory.getObject();
                    // 放入到二級緩存中
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

當(dāng) Spring 為某個(gè) Bean 填充屬性的時(shí)候,它首先會尋找需要注入對象的名稱,然后依次執(zhí)行 getSingleton() 方法得到所需注入的對象,而獲取對象的過程就是先從一級緩存中獲取,一級緩存中沒有就從二級緩存中獲取,二級緩存中沒有就從三級緩存中獲取,如果三級緩存中也沒有,那么就會去執(zhí)行 doCreateBean() 方法創(chuàng)建這個(gè) Bean。

二級緩存能夠解決循環(huán)依賴嗎

第三級緩存的目的是為了延遲代理對象的創(chuàng)建,因?yàn)槿绻麤]有依賴循環(huán)的話,那么就不需要為其提前創(chuàng)建代理,可以將它延遲到初始化完成之后再創(chuàng)建。

既然目的只是延遲的話,那么我們是不是可以不延遲創(chuàng)建,而是在實(shí)例化完成之后,就為其創(chuàng)建代理對象,這樣我們就不需要第三級緩存了。因此,我們可以將addSingletonFactory() 方法進(jìn)行改造。

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
        if (!this.singletonObjects.containsKey(beanName)) { // 判斷一級緩存中不存在此對象
            object o = singletonFactory.getObject(); // 直接從工廠中獲取 Bean
            this.earlySingletonObjects.put(beanName, o); // 添加至二級緩存中
            this.registeredSingletons.add(beanName);
        }
    }
}

這樣的話,每次實(shí)例化完 Bean 之后就直接去創(chuàng)建代理對象,并添加到二級緩存中。測試結(jié)果是完全正常的,Spring 的初始化時(shí)間應(yīng)該也是不會有太大的影響,因?yàn)槿绻?Bean 本身不需要代理的話,是直接返回原始 Bean 的,并不需要走復(fù)雜的創(chuàng)建代理 Bean 的流程。

測試證明,二級緩存也是可以解決循環(huán)依賴的。為什么 Spring 不選擇二級緩存,而要額外多添加一層緩存呢?

如果 Spring 選擇二級緩存來解決循環(huán)依賴的話,那么就意味著所有 Bean 都需要在實(shí)例化完成之后就立馬為其創(chuàng)建代理,而 Spring 的設(shè)計(jì)原則是在 Bean 初始化完成之后才為其創(chuàng)建代理。所以,Spring 選擇了三級緩存。但是因?yàn)檠h(huán)依賴的出現(xiàn),導(dǎo)致了 Spring 不得不提前去創(chuàng)建代理,因?yàn)槿绻惶崆皠?chuàng)建代理對象,那么注入的就是原始對象,這樣就會產(chǎn)生錯(cuò)誤。

解決循環(huán)依賴總結(jié)

Spring設(shè)計(jì)了三級緩存來解決循環(huán)依賴問題。

  • 第一級緩存里面存儲完整的bean實(shí)例,這些實(shí)例是可以直接被使用的;
  • 第二級緩存里面存儲的實(shí)例化以后但是還沒有設(shè)置屬性值的bean實(shí)例,也就是bean里面的依賴注入還沒有做;
  • 第三級緩存用來存放bean工廠,它主要用來生成原始bean對象,并且放到第二個(gè)緩存里面。

三級緩存的核心思想就是把bean的實(shí)例化和bean里面的依賴注入進(jìn)行分離,采用一級緩存存儲完整的bean實(shí)例,采用二級緩存來儲存不完整的bean實(shí)例。通過不完整的bean實(shí)例作為突破口,解決循環(huán)依賴問題。至于第三級緩存,主要是解決代理對象的循環(huán)依賴問題。

spring無法解決的循環(huán)依賴場景

  • 多實(shí)例的Setter注入導(dǎo)致的循環(huán)依賴,需要把bean改成單例。
  • 構(gòu)造器注入導(dǎo)致的循環(huán)依賴,可以通過@Lazy注解。
  • DependsOn導(dǎo)致的循環(huán)依賴,找到注解循環(huán)依賴的地方,使它不循環(huán)依賴。
  • 單例代理對象Setter注入導(dǎo)致的循環(huán)依賴,可以使用 @Lazy注解;或者使用 @DependsOn注解指定加載先后關(guān)系。

多例、構(gòu)造器注入為什么不能解決循環(huán)依賴?

因?yàn)檠h(huán)依賴的原理是實(shí)例化后提前暴露的引用,這兩種情況還沒實(shí)例化

以上就是Spring三級緩存思想解決循環(huán)依賴總結(jié)分析的詳細(xì)內(nèi)容,更多關(guān)于Spring解決循環(huán)依賴的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • java8 toMap問題(key重復(fù)如何解決)

    java8 toMap問題(key重復(fù)如何解決)

    這篇文章主要介紹了java8 toMap問題(key重復(fù)如何解決),具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-05-05
  • SpringBoot實(shí)現(xiàn)熱部署Community的示例代碼

    SpringBoot實(shí)現(xiàn)熱部署Community的示例代碼

    本文主要介紹了SpringBoot實(shí)現(xiàn)熱部署Community的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • 最新版IDEA配置 Tomcat的詳細(xì)過程

    最新版IDEA配置 Tomcat的詳細(xì)過程

    本文介紹如何在IDEA中配置Tomcat服務(wù)器,并創(chuàng)建Web項(xiàng)目,首先檢查Tomcat是否安裝完成,然后在IDEA中創(chuàng)建Web項(xiàng)目并添加Web結(jié)構(gòu),接著,配置編譯輸出路徑和依賴,將Tomcat的jar包復(fù)制到項(xiàng)目中,最后,配置Tomcat服務(wù)器并運(yùn)行項(xiàng)目,本文給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧
    2025-01-01
  • SpringBoot所管理的依賴和需要的依賴沖突問題及解決

    SpringBoot所管理的依賴和需要的依賴沖突問題及解決

    這篇文章主要介紹了SpringBoot所管理的依賴和需要的依賴沖突問題及解決過程,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2026-05-05
  • Kafka的基本使用及環(huán)境安裝

    Kafka的基本使用及環(huán)境安裝

    Kafka是分布式流處理平臺,用于解耦、異步、削峰填谷等,支持高吞吐、持久化、可擴(kuò)展,安裝需先啟動(dòng)ZooKeeper,通過命令行或JavaAPI操作主題實(shí)現(xiàn)消息發(fā)送與消費(fèi),本文給大家介紹Kafka的基本使用及環(huán)境安裝,感興趣的朋友一起看看吧
    2025-07-07
  • RabbitMQ在Java中的應(yīng)用與實(shí)踐示例展示

    RabbitMQ在Java中的應(yīng)用與實(shí)踐示例展示

    本文將詳細(xì)闡述如何在Java項(xiàng)目中集成RabbitMQ,并通過具體的代碼示例來展示其基礎(chǔ)用法,幫助讀者快速上手,感興趣的朋友跟隨小編一起看看吧
    2026-04-04
  • Spring Boot控制層參數(shù)綁定@RequestPart 注解的使用

    Spring Boot控制層參數(shù)綁定@RequestPart 注解的使用

    @RequestPart是一個(gè)非常重要但常被忽略的注解,專門用于處理multipart/form-data請求中的復(fù)雜數(shù)據(jù)類型,下面給大家介紹Spring Boot控制層參數(shù)綁定@RequestPart 注解的使用,感興趣的朋友跟隨小編一起看看吧
    2026-02-02
  • SpringMVC處理Form表單實(shí)例

    SpringMVC處理Form表單實(shí)例

    這篇文章主要介紹了使用SpringMVC處理Form表單實(shí)例,非常具有參考借鑒價(jià)值,感興趣的朋友一起學(xué)習(xí)吧
    2016-10-10
  • Jackson的用法實(shí)例分析

    Jackson的用法實(shí)例分析

    這篇文章主要介紹了Jackson的用法實(shí)例分析,用于處理Java的json格式數(shù)據(jù)非常實(shí)用,需要的朋友可以參考下
    2014-08-08
  • Java?Apache?common-pool對象池介紹

    Java?Apache?common-pool對象池介紹

    這篇文章主要介紹了Java Apache?common-pool對象池介紹,文章通過圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,感興趣的小伙伴可以參考一下
    2022-09-09

最新評論

清河县| 临桂县| 利川市| 万源市| 甘孜| 额尔古纳市| 江山市| 宜兴市| 陵川县| 漳浦县| 涿州市| 楚雄市| 东山县| 永寿县| 衡山县| 沙河市| 新泰市| 铜川市| 西乌珠穆沁旗| 阆中市| 扎鲁特旗| 洛隆县| 会昌县| 九龙县| 渝中区| 且末县| 乌鲁木齐市| 滨州市| 高邮市| 怀来县| 葵青区| 武城县| 海晏县| 皮山县| 佛教| 梁平县| 淮北市| 石柱| 马山县| 驻马店市| 隆林|