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

Spring需要三個(gè)級別緩存解決循環(huán)依賴原理解析

 更新時(shí)間:2023年02月16日 14:31:04   作者:念念清晰  
這篇文章主要為大家介紹了Spring需要三個(gè)級別緩存解決循環(huán)依賴原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

導(dǎo)學(xué)

Spring的三級緩存是繞不過去的一個(gè)坎兒。面試也經(jīng)常被問到。而網(wǎng)文大多都在講Spring三級緩存的用途,而分析的很好的很少。

接下來整篇文章分析下:Spring為什么要使用三級緩存解決循環(huán)依賴,而不是二級緩存或是一級緩存

先來明白一下Spring實(shí)例化一個(gè)Bean的過程中幾個(gè)重要概念

getBean

通過getBean方法獲取單例Bean,每個(gè)bean的創(chuàng)建都是從該方法開始的。

getSingleton

Spring中最最重要的核心邏輯,沒有之一。該方法依次從一級緩存、二級緩存、三級緩存中獲取單例bean對象。

注意:如果從三級緩存中獲取到對象之后,就會(huì)被立即移動(dòng)到二級緩存。下面是源碼

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
 // Quick check for existing instance without full singleton lock
 Object singletonObject = this.singletonObjects.get(beanName);
 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
  singletonObject = this.earlySingletonObjects.get(beanName);
  if (singletonObject == null && allowEarlyReference) {
   synchronized (this.singletonObjects) {
    // Consistent creation of early reference within full singleton lock
    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) {
       singletonObject = singletonFactory.getObject();
       this.earlySingletonObjects.put(beanName, singletonObject);
       this.singletonFactories.remove(beanName);
      }
     }
    }
   }
  }
 }
 return singletonObject;
}

三個(gè)級別緩存作用

一級緩存:存放最終的單例Bean,里面所有的Bean都是直接能使用的。(這個(gè)大家一定都明白就不多說了)

三級緩存:存放一個(gè)工廠對象,這個(gè)工廠對象有一個(gè)getObject方法。工廠一般由lambda表達(dá)式組成,在工廠中主要完成了對Aop的代理。執(zhí)行一些Bean的擴(kuò)展邏輯。

二級緩存:沒錯(cuò),先介紹三級緩存是有目的的。二級緩存只有在getSingleton(前文提到)方法中,才會(huì)把三級緩存獲得的對象存入二級緩存。并且刪除三級緩存中的工廠對象。至于為什么總結(jié)里會(huì)說。

循環(huán)依賴示例

廢話不多說,先給兩個(gè)類AooBoo,這兩個(gè)類形成循環(huán)依賴,代碼簡單如下。

@Component
public class Aoo {
  @Autowired
  private Boo boo;
}
@Component
public class Boo {
  @Autowired
  private Aoo aoo;
}

循環(huán)依賴執(zhí)行流程

首先Spring會(huì)掃描指定的包,把所有標(biāo)注@Component注解的類順序的實(shí)例化。Spring啟動(dòng)時(shí),容器中沒有任何bean(當(dāng)然是有一些內(nèi)部bean的,但是這樣說便于我們理解)

下面來理解下Spring實(shí)例化Bean的順序,

  • 啟動(dòng)時(shí)
  • 檢測到Aoo開始實(shí)例化Aoo對象
  • 調(diào)用doGetBean方法實(shí)例化Aoo
  • 調(diào)用getSingleton方法,試圖從三級緩存中依次獲取bean。但是第一次肯定都為空
Object sharedInstance = getSingleton(beanName);
  • 因?yàn)榫彺鏋榭眨猿绦蚶^續(xù)往下走
sharedInstance = getSingleton(beanName, () -> {
  return createBean(beanName, mbd, args);
});

程序調(diào)用getSingleton方法創(chuàng)建Aoo對象,并且該方法傳入一個(gè)lambda表達(dá)式,這個(gè)表達(dá)式后面是會(huì)被放入三級緩存的。

  • 我們來看下getSingleton方法
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    try {
      singletonObject = singletonFactory.getObject();
      newSingleton = true;
    }
    if (newSingleton) {
      addSingleton(beanName, singletonObject);
    }
    return singletonObject;
}

可以看到該方法中試圖通過第二個(gè)參數(shù),也就是上一步的lambda表達(dá)式創(chuàng)建Aoo對象,并且創(chuàng)建完成后把Aoo對象存入一級緩存。 此時(shí)一級緩存何時(shí)加入的我們清楚了。

  • 接下來我們來看下這個(gè)lambda表達(dá)式,也即是createBean方法。而它又調(diào)用了doCreateBean方法。
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args){
   // 省略無關(guān)代碼
   return doCreateBean(beanName, mbdToUse, args);
}
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
 // 創(chuàng)建一個(gè)Aoo的包裝對象。此時(shí)Aoo是一個(gè)空殼對象其中的所有屬性均為空
 BeanWrapper instanceWrapper = null;
 Object bean = instanceWrapper.getWrappedInstance();
 // 判斷是否提前暴露(其實(shí)就是循環(huán)依賴了)
 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
 if (earlySingletonExposure) {
  // 把Aoo對象加入三級緩存?。?!
  addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
 }
 Object exposedObject = bean;
 try {
  // 給Aoo對象注入屬性
  populateBean(beanName, mbd, instanceWrapper);
  // 初始化Aoo(一些擴(kuò)展點(diǎn),與循環(huán)依賴關(guān)系不大)
  exposedObject = initializeBean(beanName, exposedObject, mbd);
 }
 if (earlySingletonExposure) {
   // 如果提前暴露對象,則嘗試從緩存中獲取。
  Object earlySingletonReference = getSingleton(beanName, false);
  if (earlySingletonReference != null) {
    exposedObject = earlySingletonReference;
  }
 }
 return exposedObject;
}

第一步:創(chuàng)建一個(gè)Aoo的空殼對象,此時(shí)Aoo其中的屬性還沒有值。

第二步:把工廠存入三級緩存,緩存的key就是對象的名稱aoo,而value是一個(gè)工廠對象的lambda表達(dá)式,這個(gè)工廠對象會(huì)返回Aoo對象。這個(gè)工廠會(huì)執(zhí)行getEarlyBeanReference方法,該方法中會(huì)完成AOP動(dòng)態(tài)代理。需要重點(diǎn)說明一下,從三級緩存中取出的對象每次都是不一樣的。因?yàn)樗敲看未砩傻摹?/strong>

第三步:執(zhí)行populateBean方法。為Aoo注入屬性,Aoo只有一個(gè)屬性Boo。Spring在注入Boo屬性的時(shí)候發(fā)現(xiàn)容器沒有Boo對象。

第四步:從這一步就循環(huán)到文章的最開始了,接下來我用小寫數(shù)字表示的步驟表示上文提到的步驟?;氐?code>3調(diào)用doGetBean方法實(shí)例化Boo。

第五步:執(zhí)行4調(diào)用getSingleton方法,試圖從三級緩存中依次獲取bean。前面說過第一次肯定都為空,這次是第一次獲取Boo對象肯定也還是空的。

第六步:依次執(zhí)行5,6,7,在7中會(huì)把Boo對象存入三級緩存中。 沒錯(cuò),任何一個(gè)Bean都會(huì)先放到三級緩存中。此時(shí)三級緩存中有兩個(gè)Bean了,分別是Aoo和Boo

第七步:在7中的populateBean方法開始給Boo注入屬性了,Boo只有一個(gè)屬性Aoo,Spring在注入Aoo屬性是會(huì)從容器中獲取,也就是調(diào)用getBean方法,此時(shí)發(fā)現(xiàn)三級緩存中有Aoo。就會(huì)從三級緩存中獲取Aoo對象并給Boo的這個(gè)屬性賦值。同時(shí)也會(huì)把Aoo對象從三級緩存移動(dòng)到二級緩存中。此時(shí)一級緩存為空、二級緩存中有Aoo對象、三級緩存中有Boo對象。此時(shí)Aoo、Boo的狀態(tài)還都是創(chuàng)建的過程中。

第八步:Boo的屬性已經(jīng)完成,回到上面的6它會(huì)把Boo對象添加到一級緩存,并從三級緩存中移除(這兒沒二級緩存啥事兒嘿嘿嘿)。 此時(shí)一級緩存中有一個(gè)對象Boo、二級緩存中有Aoo對象、三級緩存為空。

第九步:既然Boo都已經(jīng)在一級緩存當(dāng)中了,那么接著第三步來說,此時(shí)Aoo的屬性Boo也完成了賦值。此時(shí)Aoo也是一個(gè)完整對象了。但它此刻還在二級緩存當(dāng)中。

第十步:在7執(zhí)行完畢之后,回歸到6的代碼,執(zhí)行addSingleton方法把Aoo從二級緩存移動(dòng)到一級緩存當(dāng)中。至此,依賴注入完畢。一級緩存中有Aoo對象和Boo對象。二級、三級緩存為空。

思考:為什么需要三個(gè)級別的緩存來解決循環(huán)依賴

現(xiàn)在來思考一下為什么一定要是三個(gè)級別的緩存呢?我們來刪除二級緩存后看這個(gè)問題。下面我們就使用只有一級緩存和三級緩存這2個(gè)緩存來看下循環(huán)依賴的問題能不能解決。

還是Aoo和Boo兩個(gè)類循環(huán)依賴

  • Spring啟動(dòng)
  • 從一級緩存和三級緩存中獲取Aoo,緩存中沒有,則創(chuàng)建
  • 創(chuàng)建Aoo的空殼對象,并把它和工廠對象放入三級緩存中。
  • 對Aoo進(jìn)行屬性注入,發(fā)現(xiàn)Boo即不在一級緩存,也不在三級緩存。只能創(chuàng)建了
  • 創(chuàng)建Boo對象
  • 對Boo進(jìn)行屬性注入,發(fā)現(xiàn)三級緩存中有Aoo對象,直接從三級緩存中獲取。
  • Boo對象屬性裝配完成,把它從三級緩存移到一級緩存。
  • Aoo對象屬性裝配完成,此時(shí)從三級緩存中移到一級緩存。

乍一看沒啥問題是不是,其實(shí)不是的。問題出在第6步,和第8步。通過前面的講解,一定要了解到,三級緩存中每次返回的對象都不一樣。所以第6步和第8步如果都從三級緩存中獲取Aoo對象, 這兩步中的Aoo對象不是同一個(gè),Spring中的Aoo對象和Boo對象就會(huì)使這個(gè)樣子

總結(jié)

首先不是說非要三級緩存機(jī)制才能解決循環(huán)依賴,一級緩存同樣可以解決,把三級緩存代碼平鋪化就好了嘛,或者使用JVM指令,字節(jié)碼等技術(shù)完成循環(huán)依賴,但你想一下,那樣的話代碼的可讀性必然很低。所以第一個(gè)原因就是使用三級緩存解決循環(huán)依賴使得代碼可讀性非常好。

第二個(gè)原因是三級緩存中的工廠,每次getObject方法返回的實(shí)例不是同一個(gè)對象,所以需要二級緩存來緩存一下三級緩存生成的bean,這樣就保證了兩個(gè)類的屬性是環(huán)形依賴,不會(huì)破壞循環(huán)依賴。

以上就是Spring需要三個(gè)級別緩存解決循環(huán)依賴原理解析的詳細(xì)內(nèi)容,更多關(guān)于Spring三個(gè)級別緩存循環(huán)依賴的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • IDEA中osgi的開發(fā)應(yīng)用指南詳解

    IDEA中osgi的開發(fā)應(yīng)用指南詳解

    這篇文章主要介紹了IDEA中osgi的開發(fā)應(yīng)用指南詳解,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2019-04-04
  • java自定義線程池的原理簡介

    java自定義線程池的原理簡介

    這篇文章主要介紹了java自定義線程池的原理詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-08-08
  • 基于String變量的兩種創(chuàng)建方式(詳解)

    基于String變量的兩種創(chuàng)建方式(詳解)

    下面小編就為大家?guī)硪黄赟tring變量的兩種創(chuàng)建方式(詳解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-07-07
  • SpringCloud gateway request的body驗(yàn)證或修改方式

    SpringCloud gateway request的body驗(yàn)證或修改方式

    這篇文章主要介紹了SpringCloud gateway request的body驗(yàn)證或修改方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • 使用JPA自定義VO類型轉(zhuǎn)換(EntityUtils工具類)

    使用JPA自定義VO類型轉(zhuǎn)換(EntityUtils工具類)

    這篇文章主要介紹了使用JPA自定義VO類型轉(zhuǎn)換(EntityUtils工具類),具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • java實(shí)現(xiàn)簡單聊天室單人版

    java實(shí)現(xiàn)簡單聊天室單人版

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)簡單聊天室的單人版,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-07-07
  • Java實(shí)現(xiàn)圖片與二進(jìn)制的互相轉(zhuǎn)換

    Java實(shí)現(xiàn)圖片與二進(jìn)制的互相轉(zhuǎn)換

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)圖片與二進(jìn)制的互相轉(zhuǎn)換,將圖片轉(zhuǎn)二進(jìn)制再將二進(jìn)制轉(zhuǎn)成圖片,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-03-03
  • Java動(dòng)態(tài)代理Proxy應(yīng)用和底層源碼詳細(xì)分析

    Java動(dòng)態(tài)代理Proxy應(yīng)用和底層源碼詳細(xì)分析

    Java動(dòng)態(tài)代理是一種在運(yùn)行時(shí)生成代理類的機(jī)制,用于代替手動(dòng)編寫代理類的過程,這篇文章主要給大家介紹了關(guān)于Java動(dòng)態(tài)代理Proxy應(yīng)用和底層源碼詳細(xì)分析的相關(guān)資料,需要的朋友可以參考下
    2024-03-03
  • springData使用QueryDsl的示例代碼

    springData使用QueryDsl的示例代碼

    這篇文章主要介紹了springData使用QueryDsl的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2019-01-01
  • 解析Java的Spring框架的BeanPostProcessor發(fā)布處理器

    解析Java的Spring框架的BeanPostProcessor發(fā)布處理器

    這篇文章主要介紹了Java的Spring框架的BeanPostProcessor發(fā)布處理器,Spring是Java的SSH三大web開發(fā)框架之一,需要的朋友可以參考下
    2015-12-12

最新評論

卓尼县| 永仁县| 克什克腾旗| 宁晋县| 富民县| 永定县| 桃园市| 高邮市| 和平县| 九江县| 馆陶县| 池州市| 内黄县| 会同县| 双峰县| 于都县| 江城| 宜兰市| 休宁县| 泸西县| 渝北区| 锦屏县| 广南县| 义乌市| 铜川市| 元朗区| 灵山县| 抚顺市| 庆阳市| 白山市| 韶山市| 团风县| 平阴县| 凤翔县| 赞皇县| 南阳市| 凤冈县| 迁西县| 锦屏县| 贵港市| 穆棱市|