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

spring解決循環(huán)依賴的簡(jiǎn)單方法

 更新時(shí)間:2020年09月18日 09:19:54   作者:xiaojiesir  
這篇文章主要給大家介紹了關(guān)于spring解決循環(huán)依賴的簡(jiǎn)單方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

Spring內(nèi)部如何解決循環(huán)依賴,一定是單默認(rèn)的單例Bean中,屬性互相引用的場(chǎng)景。比如幾個(gè)Bean之間的互相引用:

或者

setter方式原型,prototype

原型(Prototype)的場(chǎng)景是不支持循環(huán)依賴的,因?yàn)椤皃rototype”作用域的Bean,為每一個(gè)bean請(qǐng)求提供一個(gè)實(shí)例,Spring容器不進(jìn)行緩存,因此無(wú)法提前暴露一個(gè)創(chuàng)建中的Bean,會(huì)拋出異常。

構(gòu)造器參數(shù)循環(huán)依賴

Spring容器會(huì)將每一個(gè)正在創(chuàng)建的Bean 標(biāo)識(shí)符放在一個(gè)“當(dāng)前創(chuàng)建Bean池”中,Bean標(biāo)識(shí)符在創(chuàng)建過程中將一直保持在這個(gè)池中。

因此如果在創(chuàng)建Bean過程中發(fā)現(xiàn)自己已經(jīng)在“當(dāng)前創(chuàng)建Bean池”里時(shí)將拋出BeanCurrentlyInCreationException異常表示循環(huán)依賴;而對(duì)于創(chuàng)建完畢的Bean將從“當(dāng)前創(chuàng)建Bean池”中清除掉。

Spring容器先創(chuàng)建單例A,A依賴B,然后將A放在“當(dāng)前創(chuàng)建Bean池”中,此時(shí)創(chuàng)建B,B依賴C,然后將B放在“當(dāng)前創(chuàng)建Bean池”中,此時(shí)創(chuàng)建C,C又依賴A, 但是,此時(shí)A已經(jīng)在池中,所以會(huì)報(bào)錯(cuò),,因?yàn)樵诔刂械腂ean都是未初始化完的,所以會(huì)依賴錯(cuò)誤 ,(初始化完的Bean會(huì)從池中移除)。

setter方式單例,默認(rèn)方式

那么默認(rèn)單例的屬性注入場(chǎng)景,Spring是如何支持循環(huán)依賴的?

既然,要解決循環(huán)依賴,那肯定存在著依賴,我們假設(shè)有兩個(gè)類:

A和B,A->B,B->A,且二者是通過@Autowired相互注入的。

我們假設(shè)從A開始創(chuàng)建,那就是先創(chuàng)建A對(duì)象,然后創(chuàng)建B對(duì)象,再通過反射把B對(duì)象set到A對(duì)象的屬性上去(fieldB.set(a, b)),在創(chuàng)建B的時(shí)候發(fā)現(xiàn)它又依賴于A,這時(shí)候同樣地,我要尋找一個(gè)A對(duì)象set到B的屬性上去,但是我們的系統(tǒng)中只能存在一個(gè)A對(duì)象(單例)。

為了解決這個(gè)問題,需要再前面創(chuàng)建A對(duì)象的時(shí)候就把它保存起來(lái)?我們假設(shè)保存在緩存中,后面B對(duì)象要使用的時(shí)候先去緩存中查找一下不就OK了嘛?!

所以,解決循環(huán)依賴的方法就是保存所有創(chuàng)建的對(duì)象,后面創(chuàng)建對(duì)象的時(shí)候有依賴的情況先去緩存中找一下,找到了直接set到那個(gè)正在創(chuàng)建的對(duì)象的屬性上,沒找到就創(chuàng)建一個(gè)新的對(duì)象給那個(gè)正在創(chuàng)建的對(duì)象,并保存到緩存中。

實(shí)際上,Spring中也是這么干的,只不過它的緩存不只一個(gè),而是有四個(gè)。

具體可以看下這邊文章。

http://www.fzitv.net/article/195897.htm

Spring解決循環(huán)依賴

首先,Spring內(nèi)部維護(hù)了三個(gè)Map,也就是我們通常說(shuō)的三級(jí)緩存。

在Spring的DefaultSingletonBeanRegistry類中,類上方掛著這三個(gè)Map:

  • singletonObjects 它是我們最熟悉的朋友,俗稱“單例池”“容器”,緩存創(chuàng)建完成單例Bean的地方。
  • singletonFactories 映射創(chuàng)建Bean的原始工廠
  • earlySingletonObjects 映射Bean的早期引用,也就是說(shuō)在這個(gè)Map里的Bean不是完整的,甚至還不能稱之為“Bean”,只是一個(gè)Instance.

后兩個(gè)Map其實(shí)是“墊腳石”級(jí)別的,只是創(chuàng)建Bean的時(shí)候,用來(lái)借助了一下,創(chuàng)建完成就清掉了。

為什么成為后兩個(gè)Map為墊腳石,假設(shè)最終放在singletonObjects的Bean是你想要的一杯“涼白開”。

那么Spring準(zhǔn)備了兩個(gè)杯子,即singletonFactories和earlySingletonObjects來(lái)回“倒騰”幾番,把熱水晾成“涼白開”放到singletonObjects中

循環(huán)依賴的本質(zhì)

定義兩個(gè)類A與B:

public class A {
 private B b;

 public B getB() {
  return b;
 }

 public void setB(B b) {
  this.b = b;
 }

}
public class B {
 public A getA() {
  return a;
 }

 public void setA(A a) {
  this.a = a;
 }

 private A a;
}
public class CircularDependency {
 private static Map<String, Object> cacheMap = new HashMap<>(2);

 public static void main(String[] args) throws Exception {
  // 假裝掃描出來(lái)的對(duì)象
  Class[] classes = { A.class, B.class };
  // 假裝項(xiàng)目初始化實(shí)例化所有bean
  for (Class aClass : classes) {
   getBean(aClass);
  }
  // check
  System.out.println(getBean(B.class).getA() == getBean(A.class));
  System.out.println(getBean(A.class).getB() == getBean(B.class));
 }
 
 private static <T> T getBean(Class<T> beanClass) throws Exception{
  // 本文用類名小寫 簡(jiǎn)單代替bean的命名規(guī)則
  String beanName = beanClass.getSimpleName().toLowerCase();
  // 如果已經(jīng)是一個(gè)bean,則直接返回
  if (cacheMap.containsKey(beanName)) {
   return (T) cacheMap.get(beanName);
  }
  // 將對(duì)象本身實(shí)例化
  Object object = beanClass.getDeclaredConstructor().newInstance();
  // 放入緩存
  cacheMap.put(beanName, object);
  // 把所有字段當(dāng)成需要注入的bean,創(chuàng)建并注入到當(dāng)前bean中
  Field[] fields = object.getClass().getDeclaredFields();
  for (Field field : fields) {
   field.setAccessible(true);
   // 獲取需要注入字段的class
   Class<?> fieldClass = field.getType();
   String fieldBeanName = fieldClass.getSimpleName().toLowerCase();
   // 如果需要注入的bean,已經(jīng)在緩存Map中,那么把緩存Map中的值注入到該field即可
   // 如果緩存沒有 繼續(xù)創(chuàng)建
   field.set(object, cacheMap.containsKey(fieldBeanName) ? cacheMap.get(fieldBeanName) : getBean(fieldClass));
  }
  // 屬性填充完成,返回
  return (T) object;
 }
}

這段代碼的效果,其實(shí)就是處理了循環(huán)依賴,并且處理完成后,cacheMap中放的就是完整的“Bean”了。

總結(jié)

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

相關(guān)文章

  • java書店系統(tǒng)畢業(yè)設(shè)計(jì) 用戶模塊(3)

    java書店系統(tǒng)畢業(yè)設(shè)計(jì) 用戶模塊(3)

    這篇文章主要介紹了java書店系統(tǒng)畢業(yè)設(shè)計(jì),第三步系統(tǒng)總體設(shè)計(jì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-10-10
  • SpringBoot實(shí)現(xiàn)自定義條件注解的代碼示例

    SpringBoot實(shí)現(xiàn)自定義條件注解的代碼示例

    在Spring Boot中,條件注解是一種非常強(qiáng)大的工具,它可以根據(jù)特定的條件來(lái)選擇是否加載某個(gè)類或某個(gè)Bean,文將介紹如何在Spring Boot中實(shí)現(xiàn)自定義條件注解,并提供一個(gè)示例代碼,需要的朋友可以參考下
    2023-06-06
  • Spark調(diào)優(yōu)多線程并行處理任務(wù)實(shí)現(xiàn)方式

    Spark調(diào)優(yōu)多線程并行處理任務(wù)實(shí)現(xiàn)方式

    這篇文章主要介紹了Spark調(diào)優(yōu)多線程并行處理任務(wù)實(shí)現(xiàn)方式,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08
  • SpringBoot調(diào)用第三方WebService接口的操作技巧(.wsdl與.asmx類型)

    SpringBoot調(diào)用第三方WebService接口的操作技巧(.wsdl與.asmx類型)

    這篇文章主要介紹了SpringBoot調(diào)第三方WebService接口的操作代碼(.wsdl與.asmx類型 ),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-08-08
  • RocketMQ-延遲消息的處理流程介紹

    RocketMQ-延遲消息的處理流程介紹

    這篇文章主要介紹了RocketMQ-延遲消息的處理流程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • java中BigDecimal進(jìn)行加減乘除的基本用法

    java中BigDecimal進(jìn)行加減乘除的基本用法

    大家應(yīng)該對(duì)于不需要任何準(zhǔn)確計(jì)算精度的數(shù)字可以直接使用float或double運(yùn)算,但是如果需要精確計(jì)算的結(jié)果,則必須使用BigDecimal類,而且使用BigDecimal類也可以進(jìn)行大數(shù)的操作。下面這篇文章就給大家介紹介紹關(guān)于java中BigDecimal進(jìn)行加減乘除的基本用法。
    2016-12-12
  • java中Consumer接口的使用教程詳解

    java中Consumer接口的使用教程詳解

    Java?8?引入了?java.util.function?包,其中包含了一些常用的函數(shù)式接口,Consumer?接口是其中一個(gè)函數(shù)式接口,用于表示接受一個(gè)輸入?yún)?shù)并執(zhí)行某種操作的操作者,下面我們就來(lái)學(xué)習(xí)一下他的具體使用吧
    2023-12-12
  • java web中對(duì)json的使用詳解

    java web中對(duì)json的使用詳解

    在Java Web的開發(fā)過程中,如果希望調(diào)用Java對(duì)象轉(zhuǎn)化成JSON對(duì)象等操作,我們需要引入相關(guān)jar包,下面小編給大家?guī)?lái)了java web中對(duì)json的使用,一起看看吧
    2018-08-08
  • Java如何獲取當(dāng)前年份、月份和日期字符串

    Java如何獲取當(dāng)前年份、月份和日期字符串

    Java獲取當(dāng)前年份、月份和日期是通過Calendar類的實(shí)例對(duì)象來(lái)獲取的,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
    2024-03-03
  • Java java.sql.Timestamp時(shí)間戳案例詳解

    Java java.sql.Timestamp時(shí)間戳案例詳解

    這篇文章主要介紹了Java java.sql.Timestamp時(shí)間戳案例詳解,本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08

最新評(píng)論

洪湖市| 铅山县| 平湖市| 娄底市| 绥阳县| 会宁县| 乐平市| 遂溪县| 宜川县| 门源| 山西省| 新闻| 攀枝花市| 丰原市| 房产| 定边县| 镇原县| 阳信县| 衡阳县| 康平县| 观塘区| 霍城县| 马公市| 宕昌县| 湛江市| 阿拉善盟| 乌拉特中旗| 湘西| 商丘市| 民和| 双峰县| 图们市| 乌鲁木齐县| 彩票| 宁阳县| 东光县| 焦作市| 鸡西市| 蒲江县| 嘉义县| 赤城县|