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

詳解Spring-bean的循環(huán)依賴以及解決方式

 更新時(shí)間:2018年09月05日 10:15:47   作者:惜暮  
這篇文章主要介紹了詳解Spring-bean的循環(huán)依賴以及解決方式,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧

本文主要是分析Spring bean的循環(huán)依賴,以及Spring的解決方式。 通過這種解決方式,我們可以應(yīng)用在我們實(shí)際開發(fā)項(xiàng)目中。

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

循環(huán)依賴其實(shí)就是循環(huán)引用,也就是兩個(gè)或則兩個(gè)以上的bean互相持有對(duì)方,最終形成閉環(huán)。比如A依賴于B,B依賴于C,C又依賴于A。如下圖:

注意,這里不是函數(shù)的循環(huán)調(diào)用,是對(duì)象的相互依賴關(guān)系。循環(huán)調(diào)用其實(shí)就是一個(gè)死循環(huán),除非有終結(jié)條件。

Spring中循環(huán)依賴場景有:
(1)構(gòu)造器的循環(huán)依賴
(2)field屬性的循環(huán)依賴。

循環(huán)依賴的產(chǎn)生和解決的前提

循環(huán)依賴的產(chǎn)生可能有很多種情況,例如:

  • A的構(gòu)造方法中依賴了B的實(shí)例對(duì)象,同時(shí)B的構(gòu)造方法中依賴了A的實(shí)例對(duì)象
  • A的構(gòu)造方法中依賴了B的實(shí)例對(duì)象,同時(shí)B的某個(gè)field或者setter需要A的實(shí)例對(duì)象,以及反之
  • A的某個(gè)field或者setter依賴了B的實(shí)例對(duì)象,同時(shí)B的某個(gè)field或者setter依賴了A的實(shí)例對(duì)象,以及反之

當(dāng)然,Spring對(duì)于循環(huán)依賴的解決不是無條件的,首先前提條件是針對(duì)scope單例并且沒有顯式指明不需要解決循環(huán)依賴的對(duì)象,而且要求該對(duì)象沒有被代理過。同時(shí)Spring解決循環(huán)依賴也不是萬能,以上三種情況只能解決兩種,第一種在構(gòu)造方法中相互依賴的情況Spring也無力回天。結(jié)論先給在這,下面來看看Spring的解決方法,知道了解決方案就能明白為啥第一種情況無法解決了。

2. 怎么檢測是否存在循環(huán)依賴

檢測循環(huán)依賴相對(duì)比較容易,Bean在創(chuàng)建的時(shí)候可以給該Bean打標(biāo),如果遞歸調(diào)用回來發(fā)現(xiàn)正在創(chuàng)建中的話,即說明了循環(huán)依賴了。

3. Spring怎么解決循環(huán)依賴

Spring的循環(huán)依賴的理論依據(jù)其實(shí)是基于Java的引用傳遞,當(dāng)我們獲取到對(duì)象的引用時(shí),對(duì)象的field或則屬性是可以延后設(shè)置的(但是構(gòu)造器必須是在獲取引用之前)。

Spring的單例對(duì)象的初始化主要分為三步:

 

(1)createBeanInstance:實(shí)例化,其實(shí)也就是調(diào)用對(duì)象的構(gòu)造方法實(shí)例化對(duì)象

(2)populateBean:填充屬性,這一步主要是多bean的依賴屬性進(jìn)行填充

(3)initializeBean:調(diào)用spring xml中的init 方法。

從上面講述的單例bean初始化步驟我們可以知道,循環(huán)依賴主要發(fā)生在第一、第二部。也就是構(gòu)造器循環(huán)依賴和field循環(huán)依賴。

那么我們要解決循環(huán)引用也應(yīng)該從初始化過程著手,對(duì)于單例來說,在Spring容器整個(gè)生命周期內(nèi),有且只有一個(gè)對(duì)象,所以很容易想到這個(gè)對(duì)象應(yīng)該存在Cache中,Spring為了解決單例的循環(huán)依賴問題,使用了三級(jí)緩存。

首先我們看源碼,三級(jí)緩存主要指:

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

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

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

這三級(jí)緩存分別指:

  • singletonFactories : 單例對(duì)象工廠的cache
  • earlySingletonObjects :提前暴光的單例對(duì)象的Cache
  • singletonObjects:單例對(duì)象的cache

我們?cè)趧?chuàng)建bean的時(shí)候,首先想到的是從cache中獲取這個(gè)單例的bean,這個(gè)緩存就是singletonObjects。主要調(diào)用方法就就是:

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 != NULL_OBJECT ? singletonObject : null);
}

上面的代碼需要解釋兩個(gè)參數(shù):

  1. isSingletonCurrentlyInCreation()判斷當(dāng)前單例bean是否正在創(chuàng)建中,也就是沒有初始化完成(比如A的構(gòu)造器依賴了B對(duì)象所以得先去創(chuàng)建B對(duì)象, 或則在A的populateBean過程中依賴了B對(duì)象,得先去創(chuàng)建B對(duì)象,這時(shí)的A就是處于創(chuàng)建中的狀態(tài)。)
  2. allowEarlyReference 是否允許從singletonFactories中通過getObject拿到對(duì)象

分析getSingleton()的整個(gè)過程,Spring首先從一級(jí)緩存singletonObjects中獲取。如果獲取不到,并且對(duì)象正在創(chuàng)建中,就再從二級(jí)緩存earlySingletonObjects中獲取。如果還是獲取不到且允許singletonFactories通過getObject()獲取,就從三級(jí)緩存singletonFactory.getObject()(三級(jí)緩存)獲取,如果獲取到了則:

this.earlySingletonObjects.put(beanName, singletonObject);
            this.singletonFactories.remove(beanName);

從singletonFactories中移除,并放入earlySingletonObjects中。其實(shí)也就是從三級(jí)緩存移動(dòng)到了二級(jí)緩存。

從上面三級(jí)緩存的分析,我們可以知道,Spring解決循環(huán)依賴的訣竅就在于singletonFactories這個(gè)三級(jí)cache。這個(gè)cache的類型是ObjectFactory,定義如下:

public interface ObjectFactory<T> {
  T getObject() throws BeansException;
}

這個(gè)接口在下面被引用

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);
    }
  }
}

這里就是解決循環(huán)依賴的關(guān)鍵,這段代碼發(fā)生在createBeanInstance之后,也就是說單例對(duì)象此時(shí)已經(jīng)被創(chuàng)建出來(調(diào)用了構(gòu)造器)。這個(gè)對(duì)象已經(jīng)被生產(chǎn)出來了,雖然還不完美(還沒有進(jìn)行初始化的第二步和第三步),但是已經(jīng)能被人認(rèn)出來了(根據(jù)對(duì)象引用能定位到堆中的對(duì)象),所以Spring此時(shí)將這個(gè)對(duì)象提前曝光出來讓大家認(rèn)識(shí),讓大家使用。

這樣做有什么好處呢?讓我們來分析一下“A的某個(gè)field或者setter依賴了B的實(shí)例對(duì)象,同時(shí)B的某個(gè)field或者setter依賴了A的實(shí)例對(duì)象”這種循環(huán)依賴的情況。A首先完成了初始化的第一步,并且將自己提前曝光到singletonFactories中,此時(shí)進(jìn)行初始化的第二步,發(fā)現(xiàn)自己依賴對(duì)象B,此時(shí)就嘗試去get(B),發(fā)現(xiàn)B還沒有被create,所以走create流程,B在初始化第一步的時(shí)候發(fā)現(xiàn)自己依賴了對(duì)象A,于是嘗試get(A),嘗試一級(jí)緩存singletonObjects(肯定沒有,因?yàn)锳還沒初始化完全),嘗試二級(jí)緩存earlySingletonObjects(也沒有),嘗試三級(jí)緩存singletonFactories,由于A通過ObjectFactory將自己提前曝光了,所以B能夠通過ObjectFactory.getObject拿到A對(duì)象(雖然A還沒有初始化完全,但是總比沒有好呀),B拿到A對(duì)象后順利完成了初始化階段1、2、3,完全初始化之后將自己放入到一級(jí)緩存singletonObjects中。此時(shí)返回A中,A此時(shí)能拿到B的對(duì)象順利完成自己的初始化階段2、3,最終A也完成了初始化,進(jìn)去了一級(jí)緩存singletonObjects中,而且更加幸運(yùn)的是,由于B拿到了A的對(duì)象引用,所以B現(xiàn)在hold住的A對(duì)象完成了初始化。

知道了這個(gè)原理時(shí)候,肯定就知道為啥Spring不能解決“A的構(gòu)造方法中依賴了B的實(shí)例對(duì)象,同時(shí)B的構(gòu)造方法中依賴了A的實(shí)例對(duì)象”這類問題了!因?yàn)榧尤雜ingletonFactories三級(jí)緩存的前提是執(zhí)行了構(gòu)造器,所以構(gòu)造器的循環(huán)依賴沒法解決。

以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 圖解如何在Spring Boot中使用JSP頁面

    圖解如何在Spring Boot中使用JSP頁面

    這篇文章主要介紹了圖解如何在Spring Boot中使用JSP頁面,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-07-07
  • SpringDataJpa like查詢無效的解決

    SpringDataJpa like查詢無效的解決

    這篇文章主要介紹了SpringDataJpa like查詢無效的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • java實(shí)現(xiàn)自定義時(shí)鐘并實(shí)現(xiàn)走時(shí)功能

    java實(shí)現(xiàn)自定義時(shí)鐘并實(shí)現(xiàn)走時(shí)功能

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)自定義時(shí)鐘并實(shí)現(xiàn)走時(shí)功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • @Valid 無法校驗(yàn)List<E>的問題

    @Valid 無法校驗(yàn)List<E>的問題

    這篇文章主要介紹了@Valid 無法校驗(yàn)List<E>的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • java如何獲取視頻文件的編解碼器代碼示例

    java如何獲取視頻文件的編解碼器代碼示例

    這篇文章主要給大家介紹了關(guān)于java如何獲取視頻文件的編解碼器的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用java具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2024-05-05
  • maven項(xiàng)目無法讀取到resource文件夾的問題

    maven項(xiàng)目無法讀取到resource文件夾的問題

    這篇文章主要介紹了maven項(xiàng)目無法讀取到resource文件夾的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Windows下使用IDEA搭建Hadoop開發(fā)環(huán)境的詳細(xì)方法

    Windows下使用IDEA搭建Hadoop開發(fā)環(huán)境的詳細(xì)方法

    這篇文章主要介紹了Windows下使用IDEA搭建Hadoop開發(fā)環(huán)境,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-12-12
  • 淺談javaSE 面向?qū)ο?Object類toString)

    淺談javaSE 面向?qū)ο?Object類toString)

    下面小編就為大家?guī)硪黄獪\談javaSE 面向?qū)ο?Object類toString)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-06-06
  • SpringBoot和Vue2項(xiàng)目配置https協(xié)議過程

    SpringBoot和Vue2項(xiàng)目配置https協(xié)議過程

    本文詳細(xì)介紹了SpringBoot項(xiàng)目和Vue2項(xiàng)目的部署流程及SSL證書配置,對(duì)于SpringBoot項(xiàng)目,需將.pfx文件放入resources目錄并配置server,然后打包部署,Vue2項(xiàng)目中,涉及檢查nginx的SSL模塊、編譯新的nginx文件
    2024-10-10
  • Java實(shí)現(xiàn)浪漫流星表白的示例代碼

    Java實(shí)現(xiàn)浪漫流星表白的示例代碼

    本文將利用Java語言實(shí)現(xiàn)浪漫流星表白,可以實(shí)現(xiàn)這些功能:播放音樂、自定義流星數(shù)量、飛行速度、光暈大小、流星大小,自定義表白話語,感興趣的可以學(xué)習(xí)一下
    2022-05-05

最新評(píng)論