欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring解決循環(huán)依賴的方法(三級(jí)緩存)

 更新時(shí)間:2021年11月16日 17:14:14   作者:尋說  
今天,我們要說的是spring是如何解決循環(huán)依賴的。對(duì)于一個(gè)問題說解決之前,我們首先要先明確形成問題的本因。那么循環(huán)依賴,何為循環(huán)依賴呢?感興趣的朋友跟隨小編一起看看吧

  說起Spring,作為流水線上裝配工的小碼農(nóng),可能是我們最熟悉不過的一種技術(shù)框架。但是對(duì)于Spring到底是個(gè)什么東西,我猜作為大多數(shù)的你可能跟我一樣,只知道IOC、DI,卻并不明白這其中的原理究竟是怎樣的。在這兒你可能想得完整的關(guān)于Spring相關(guān)的知識(shí),但是我要告訴你對(duì)不起。這里不是教程,只能作為你窺探spring核心的窗口。我不做教程,因?yàn)榫W(wǎng)上的教程、源碼解析太多,你可以自行選擇學(xué)習(xí)。但我要提醒你的是,看再多的教程也不如你一次的主動(dòng)去追蹤源碼。

  好了,廢話說了這么多就是提醒你這里不是一個(gè)教程。只是一個(gè)描繪似的談?wù)?,期間會(huì)有一些知識(shí)或舉例缺陷,所以期望你的指正。

  今天,我們要說的是spring是如何解決循環(huán)依賴的。對(duì)于一個(gè)問題說解決之前,我們首先要先明確形成問題的本因。那么循環(huán)依賴,何為循環(huán)依賴呢?

  這里我們先借用一張圖來通過視覺感受一下,看圖:

  

  其實(shí),通過上面圖片我想你應(yīng)該能看圖說話了,所謂的循環(huán)依賴其實(shí)就是一種死循環(huán)。想象一下生活中的例子就是,你作為一個(gè)猛男喜歡一個(gè)蘿莉,而蘿莉卻愛上了你的基友娘炮,但是娘炮心理卻一直想著和你去澡堂洗澡時(shí)撿你扔的肥皂。

  是的,這就是循環(huán)依賴的本因。當(dāng)spring啟動(dòng)在解析配置創(chuàng)建bean的過程中。首先在初始化A的時(shí)候發(fā)現(xiàn)需要引用B,然后去初始化B的時(shí)候又發(fā)現(xiàn)引用了C,然后又去初始化C卻發(fā)現(xiàn)一個(gè)操蛋的結(jié)果,C引用了A。它又去初始化A一次循環(huán)無窮盡,如你們這該死的變態(tài)三角關(guān)系一樣。

  既然形成了這種看起來無法 解決的三角關(guān)系,那么有什么辦法解決呢?相信聰明的你在面對(duì)這樣尷尬的境地,已經(jīng)開始思考解決方案了。想不想的出來沒關(guān)系,我們看看spring的大神們是如何來解決這個(gè)問題的。

  Spring解決循環(huán)依賴的方法就是如題所述的三級(jí)緩存、預(yù)曝光。

  Spring的三級(jí)緩存主要是singletonObjects、earlySingletonObjects、singletonFactories這三個(gè)Map:

  代碼 1-1:

/** Cache of singleton objects: bean name --> bean instance */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

    /** Cache of singleton factories: bean name --> ObjectFactory */
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

    /** Cache of early singleton objects: bean name --> bean instance */
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

我們知道了Spring為解決循環(huán)依賴所定義的三級(jí)緩存了,那么我們就來看看它是如何通過這三級(jí)緩存來解決這個(gè)問題的。

代碼 1-2:

@Nullable
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        Object singletonObject = this.singletonObjects.get(beanName);  //首先通過beanName從一級(jí)緩存獲取bean
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {  //如果一級(jí)緩存中沒有,并且beanName映射的bean正在創(chuàng)建中
            synchronized (this.singletonObjects) {
                singletonObject = this.earlySingletonObjects.get(beanName);  //從二級(jí)緩存中獲取
                if (singletonObject == null && allowEarlyReference) {  //二級(jí)緩存也沒有
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);  //從三級(jí)緩存獲取
                    if (singletonFactory != null) {
                        singletonObject = singletonFactory.getObject();  //獲取到bean
                        this.earlySingletonObjects.put(beanName, singletonObject);  //將獲取的bean提升至二級(jí)緩存
                        this.singletonFactories.remove(beanName);  //從三級(jí)緩存刪除
                    }
                }
            }
        }
        return singletonObject;
    }

上面的方法就是Spring獲取single bean的過程,其中的一些方法的解釋我就直接借用其它博主的文摘了:

  • isSingletonCurrentlyInCreation():判斷當(dāng)前 singleton bean 是否處于創(chuàng)建中。bean 處于創(chuàng)建中也就是說 bean 在初始化但是沒有完成初始化,有一個(gè)這樣的過程其實(shí)和 Spring 解決 bean 循環(huán)依賴的理念相輔相成,因?yàn)?Spring 解決 singleton bean 的核心就在于提前曝光 bean。
  • allowEarlyReference:從字面意思上面理解就是允許提前拿到引用。其實(shí)真正的意思是是否允許從 singletonFactories 緩存中通過getObject()拿到對(duì)象,為什么會(huì)有這樣一個(gè)字段呢?原因就在于 singletonFactories 才是 Spring 解決 singleton bean 的訣竅所在。

好了,說道這里我們來縷清一下當(dāng)我們執(zhí)行下面代碼獲取一個(gè)name為 user 的bean時(shí)Spring都經(jīng)過了怎樣的過程。

代碼 1-3:

ClassPathResource resource = new ClassPathResource("bean.xml");
 DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
 XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
 reader.loadBeanDefinitions(resource);
 UserBean user = (UserBean) factory.getBean("user");

通過追蹤源碼我們發(fā)現(xiàn)getBean()方法執(zhí)行后會(huì)調(diào)用到AbstractBeanFactory.doGetBean()方法,在此方法中調(diào)用了我們上文提到的關(guān)鍵getSingleton()。因?yàn)殚_始啟動(dòng)項(xiàng)目,獲取第一個(gè)bean時(shí)緩存都是空的,所以直接返回一個(gè)null。追蹤源碼發(fā)現(xiàn),在doGetBean()后面會(huì)調(diào)用到AbstractAutowireCapableBeanFactory.doCreateBean()方法進(jìn)行bean創(chuàng)建,詳細(xì)流程就自行追蹤源碼。在這個(gè)方法中有一段代碼:

代碼 1-4:

// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                isSingletonCurrentlyInCreation(beanName)); //通過條件判斷該bean是否允許提前曝露
   if (earlySingletonExposure) {
      if (logger.isTraceEnabled()) {
                logger.trace("Eagerly caching bean '" + beanName +
                        "' to allow for resolving potential circular references");
     }
     addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); //允許提前暴露的bean 添加到三級(jí)緩存
}

通過上面的代碼我們發(fā)現(xiàn),可以提前暴露的bean通過addSingletonFactory()方法添加到了三級(jí)緩存SingletonFactories 中,我們看一下它是怎樣操作的。

代碼 1-5:

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);  //添加到三級(jí)緩存
                this.earlySingletonObjects.remove(beanName);
                this.registeredSingletons.add(beanName);
            }
        }
    }

此時(shí),我們的user這個(gè)bean就被加入到了三級(jí)緩存中,那么什么樣bean的才會(huì)被加入到三級(jí)緩存中呢?就是代碼 1-4中的三個(gè)判斷條件:

  • 單例
  • 允許循環(huán)引用的bean
  • 當(dāng)前 bean 正在創(chuàng)建中

到這里user這個(gè)bean已經(jīng)創(chuàng)建,但是它還不是一個(gè)完整的bean,還需要后續(xù)的初始化。但是這不影響其它的bean引用它,假設(shè)user為開始圖中的A,那么當(dāng)在初始化A(user)的時(shí)候,發(fā)現(xiàn)A中有一個(gè)屬性B,在調(diào)用方法applyPropertyValues()去設(shè)置這個(gè)B的時(shí)候。會(huì)發(fā)現(xiàn)B還沒創(chuàng)建,Spring就會(huì)在重復(fù)創(chuàng)建A的流程調(diào)用doCreate()來創(chuàng)建B,然后添加到三級(jí)緩存,設(shè)置B的屬性C時(shí),發(fā)現(xiàn)C也還沒創(chuàng)建,接著重復(fù)前述doCreate()步驟進(jìn)行C的創(chuàng)建。C創(chuàng)建完成,進(jìn)行初始化發(fā)現(xiàn)C引用了A,這時(shí)關(guān)鍵的地方就是上面的代碼1-2處。

在C設(shè)置屬性A的時(shí)候,調(diào)用getSingleton()獲取bean時(shí),因?yàn)锳已經(jīng)在代碼1-4處添加到了三級(jí)緩存中,C可以直接獲取到A的實(shí)例并設(shè)置成功后,繼續(xù)完成自己創(chuàng)建。初始化完成后,調(diào)用如下方法將自己添加到一級(jí)緩存。

代碼 1-6:

protected void addSingleton(String beanName, Object singletonObject) {
  synchronized (this.singletonObjects) {
      this.singletonObjects.put(beanName, singletonObject);
      this.singletonFactories.remove(beanName);
      this.earlySingletonObjects.remove(beanName);
      this.registeredSingletons.add(beanName);
  }
}

至此,Spring對(duì)于循環(huán)依賴的解決就說完了。總結(jié)來看,就想你們?nèi)顷P(guān)系中那樣,娘炮在纏著你撿肥皂的時(shí)候,你可以先把肥皂扔到地上安撫一下他。然后,再去追求你要的蘿莉。然而他也可能做出一個(gè)假象安撫蘿莉,而蘿莉也可能把你加入云胎庫(kù)。最后的結(jié)果要等真正使用時(shí)才知道……

到此這篇關(guān)于Spring解決循環(huán)依賴的的方法(三級(jí)緩存)的文章就介紹到這了,更多相關(guān)Spring循環(huán)依賴內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java 獲取路徑的各種方法(總結(jié))

    java 獲取路徑的各種方法(總結(jié))

    下面小編就為大家?guī)硪黄猨ava 獲取路徑的各種方法(總結(jié))。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-08-08
  • Java使用list集合remove需要注意的事項(xiàng)(使用示例)

    Java使用list集合remove需要注意的事項(xiàng)(使用示例)

    List集合的一個(gè)特點(diǎn)是它其中的元素是有序的,也就是說元素的下標(biāo)是根據(jù)插入的順序來的,在刪除頭部或者中間的一個(gè)元素后,后面的元素下標(biāo)會(huì)往前移動(dòng),本文給大家介紹Java使用list集合remove需要注意的事項(xiàng),感興趣的朋友一起看看吧
    2022-01-01
  • mybatis 加載配置文件的方法(兩種方式)

    mybatis 加載配置文件的方法(兩種方式)

    這篇文章主要介紹了mybatis 加載配置文件的方法,通過實(shí)例代碼給大家介紹了mybatis 加載配置文件的兩種方式,需要的朋友可以參考下
    2017-12-12
  • SpringJPA?做分頁(yè)條件查詢的代碼實(shí)踐

    SpringJPA?做分頁(yè)條件查詢的代碼實(shí)踐

    相信小伙伴們的項(xiàng)目很多都用到SpringJPA框架的吧,對(duì)于單表的增刪改查利用jpa是很方便的,但是對(duì)于條件查詢并且分頁(yè)?是不是很多小伙伴不經(jīng)常寫到,今天給大家分享SpringJPA?做分頁(yè)條件查詢的案例代碼,感興趣的朋友一起看看吧
    2024-03-03
  • 淺談Spring 的Controller 是單例or多例

    淺談Spring 的Controller 是單例or多例

    這篇文章主要介紹了淺談Spring 的Controller 是單例or多例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • 基于SpringMVC中的路徑參數(shù)和URL參數(shù)實(shí)例

    基于SpringMVC中的路徑參數(shù)和URL參數(shù)實(shí)例

    這篇文章主要介紹了基于SpringMVC中的路徑參數(shù)和URL參數(shù)實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • java8 LocalDate 使用詳解

    java8 LocalDate 使用詳解

    這篇文章主要介紹了java8 LocalDate 使用詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • 一文吃透Spring?Cloud?gateway自定義錯(cuò)誤處理Handler

    一文吃透Spring?Cloud?gateway自定義錯(cuò)誤處理Handler

    這篇文章主要為大家介紹了一文吃透Spring?Cloud?gateway自定義錯(cuò)誤處理Handler方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-03-03
  • springmvc實(shí)現(xiàn)跨服務(wù)器文件上傳功能

    springmvc實(shí)現(xiàn)跨服務(wù)器文件上傳功能

    這篇文章主要為大家詳細(xì)介紹了springmvc實(shí)現(xiàn)跨服務(wù)器文件上傳功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-08-08
  • java中注解的實(shí)現(xiàn)原理詳解

    java中注解的實(shí)現(xiàn)原理詳解

    這篇文章主要介紹了java中注解的實(shí)現(xiàn)原理詳解,注解也叫元數(shù)據(jù),例如我們常見的@Override和@Deprecated,注解是JDK1.5版本開始引入的一個(gè)特性,用于對(duì)代碼進(jìn)行說明,可以對(duì)包、類、接口、字段、方法參數(shù)、局部變量等進(jìn)行注解,需要的朋友可以參考下
    2023-10-10

最新評(píng)論