亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

淺入淺出的講解Spring循環(huán)依賴問(wèn)題

 更新時(shí)間:2021年10月12日 16:16:44   作者:一條coding  
循環(huán)依賴其實(shí)就是循環(huán)引用,也就是兩個(gè)或則兩個(gè)以上的bean互相持有對(duì)方,最終形成閉環(huán),下面這篇文章主要給大家介紹了關(guān)于Spring循環(huán)依賴問(wèn)題的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下

前言

最近有粉絲問(wèn)到了循環(huán)依賴問(wèn)題,以后再有人問(wèn)你,拿這篇“吊打”他。

概念

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

多個(gè)bean之間相互依賴,形成了一個(gè)閉環(huán)。比如:A依賴于B、B依賴于C、C依賴于A。

通常來(lái)說(shuō),如果問(wèn)Spring容器內(nèi)部如何解決循環(huán)依賴,一定是指默認(rèn)的單例Bean中,基于set方法構(gòu)造注入的屬性互相引用的場(chǎng)景。

循環(huán)依賴的種類及能否解決如下:


名稱 是否可解決循環(huán)依賴
構(gòu)造器循環(huán)依賴
Setter循環(huán)依賴
Prototype作用域的循環(huán)依賴

報(bào)錯(cuò)信息

Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name ‘myDao': Requested bean is currently in creation: Is there an unresolvable circular reference?

翻譯一下

通過(guò)構(gòu)造函數(shù)參數(shù) 0 表示的依賴關(guān)系未得到滿足;嵌套的異常是 創(chuàng)建名稱為'myDao'的bean時(shí)出錯(cuò)。請(qǐng)求的Bean目前正在創(chuàng)建中。是否存在一個(gè)無(wú)法解決的循環(huán)引用?

異常信息:bean當(dāng)前創(chuàng)建異常org.springframework.beans.factory.BeanCurrentlyInCreationException。

通俗版理解

兩人對(duì)峙

現(xiàn)在甲乙兩個(gè)人,互相對(duì)峙,甲說(shuō)乙先放,乙說(shuō)甲先放。就是不開(kāi)槍。

哎,就是玩!

相信這個(gè)場(chǎng)景大家在電視劇里都見(jiàn)過(guò)吧,最后一般是“反派死于話多”。

但是回到我們 spring里,我們是不希望有人死亡的,也就是必須兩個(gè)bean都創(chuàng)建出來(lái),怎么辦?

必須有一人妥協(xié)

解決方案就是:必須有一個(gè)人先妥協(xié)。

甲說(shuō):我退一步,我先把彈夾卸了,你把槍放下。

乙一聽(tīng)就感動(dòng)了,滿含熱淚的拿槍放下了。

甲一看乙沒(méi)有打自己,也熱淚盈眶,兩人緊緊相擁。

從此過(guò)上了幸福美滿的生活……

Spring版理解

回到我們spring里,先回顧一下bean的生命周期:

  • 實(shí)例化
  • 屬性賦值
  • 初始化
  • 銷毀

簡(jiǎn)單理解一下的上面的過(guò)程

實(shí)例化和初始化什么區(qū)別?

是不是只差了中間賦值的過(guò)程,那只實(shí)例化的bean可以使用嗎?

當(dāng)然不可以!

也就是說(shuō)只實(shí)例化的bean是一個(gè)半成品,初始化之后才是成品,才可以使用。

現(xiàn)在A依賴B,B依賴A。

A對(duì)B說(shuō):我要完整的你

b也對(duì)a:我要完整的你

ok,兩人打起來(lái)了,拿槍對(duì)峙。怎么解決?是不是得一個(gè)人妥協(xié)。

a說(shuō):算了吧,你給我個(gè)你的半成品,我將就一下。

b心里尋思,他用我的半成品創(chuàng)建一個(gè)完整的a,然后我就可以創(chuàng)建了。

心里這么想,嘴上就爽快答應(yīng)著:行,沒(méi)問(wèn)題。

如此,a創(chuàng)建了完整的自己,b拿著a也完成了創(chuàng)建。

問(wèn)題解決。

真的解決了嗎?成品和半成品都存在哪里呢?

這就不得不提到大名鼎鼎的三級(jí)緩存。

三級(jí)緩存

spring提供了三級(jí)緩存來(lái)存放成品和半成品及工廠。位于DefaultSingletonBeanRegistry類中。

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {   
	/**
	*一級(jí)緩存:?jiǎn)卫?
	*存放已經(jīng)初始化的bean——成品
	*/
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
    /**
	*三級(jí)緩存:?jiǎn)卫S的高速緩存
	*存放生成bean的工廠
	*/
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
    /**
	*二級(jí)緩存:早期單例對(duì)象的高速緩存
	*存放已經(jīng)實(shí)例化但未初始化(未填充屬性)的的bean——半成品
	*/
    private final Map<String, Object> earlySingletonObjects = new HashMap(16);
}

創(chuàng)建過(guò)程(簡(jiǎn)易版)

如果你是面試突擊,建議把簡(jiǎn)易版被下來(lái)就可以應(yīng)付面試了
等有時(shí)間再看源碼版

假如A依賴B,B依賴A,那么這兩個(gè)類之間形成了一個(gè)循環(huán)依賴

  • A先開(kāi)始創(chuàng)建,通過(guò)其無(wú)參構(gòu)造方法創(chuàng)建bean的實(shí)例,并將其實(shí)例放入到「二級(jí)緩存」提前暴露出來(lái)。A停止。
  • B開(kāi)始創(chuàng)建,先去「一級(jí)緩存」找A的成品,找不到,再去「二級(jí)緩存」里找,還找不到,再去「三級(jí)緩存」里找,找到了A的創(chuàng)建工廠,通過(guò)工廠,拿到A的半成品,并將A放到「二級(jí)緩存」。
  • 拿到A后,B完成創(chuàng)建,將自己放入「一級(jí)緩存」。
  • 此時(shí)A繼續(xù)創(chuàng)建,同樣從「一級(jí)緩存」開(kāi)始找,拿到B后完成創(chuàng)建,將自己放入「一級(jí)緩存」。

創(chuàng)建過(guò)程(源碼版)

源碼版建議配合spring源碼邊debug邊食用。

1、當(dāng)我們?cè)谡{(diào)用getBean()獲取bean時(shí),實(shí)際調(diào)用的是doGetBean() 方法。doGetBean() 想要獲取 beanA ,于是調(diào)用 getSingleton() 方法從緩存中查找 beanA

2、在 getSingleton() 方法中,從「一級(jí)緩存」中查找,沒(méi)有,返回 null

3、doGetBean() 方法中獲取到 beanA 為 null ,于是走對(duì)應(yīng)的處理邏輯,調(diào)用 getSingleton() 的重載方法(參數(shù)為 ObjectFactory 的)

4、在 getSingleton()方法中,先將 beanA_name 添加到一個(gè)集合中,用于標(biāo)記該 bean 正在創(chuàng)建中,然后回調(diào)匿名內(nèi)部類的 createBean 方法

5、進(jìn)入 AbstractAutowireCapableBeanFactory#doCreateBean,先反射調(diào)用構(gòu)造器創(chuàng)建出 beanA 的實(shí)例,然后判斷,是否為單例,是否允許提前暴露引用(對(duì)于單例一般為true)、是否正在創(chuàng)建中(即是否是在第四步的集合中)判斷為 true 則將 beanA 添加到「三級(jí)緩存」中

6、對(duì) beanA 進(jìn)行屬性填充,此時(shí)檢測(cè)到 beanA 依賴于 beanB ,于是查找 beanB

7、調(diào)用 doGetBean() 方法,和上面 beanA 的過(guò)程一樣,到緩存中查詢 beanB ,沒(méi)有則創(chuàng)建,然后給 beanB 填充屬性

8、此時(shí) beanB 依賴于 beanA ,調(diào)用 getSingleton() 獲取 beanA ,依次從一級(jí)、二級(jí)、三級(jí)緩存中找、此時(shí)從「三級(jí)緩存」中獲取到 beanA 的創(chuàng)建工廠,通過(guò)創(chuàng)建工廠獲取到 singletonObject ,此時(shí)這個(gè) singletonObject 指向的就是上面在 doCreateBean() 方法中實(shí)例化的 beanA

9、這樣 beanB 就獲取到了 beanA 的依賴,于是 beanB 順利完成初始化,并將 beanA 從「三級(jí)緩存」移動(dòng)到「二級(jí)緩存」中

10、隨后 beanA 繼續(xù)他的屬性填充工作,此時(shí)也獲取到了 beanB ,beanA 也隨之完成了創(chuàng)建,回到 getSingleton() 方法中繼續(xù)向下執(zhí)行,將 beanA 從「二級(jí)緩存」移動(dòng)到「一級(jí)緩存」中

最后

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

相關(guān)文章

  • java項(xiàng)目jar包與jdk的版本不兼容的問(wèn)題解決

    java項(xiàng)目jar包與jdk的版本不兼容的問(wèn)題解決

    這篇文章主要介紹了java項(xiàng)目jar包與jdk的版本不兼容的問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-10-10
  • Java 認(rèn)識(shí)異常并掌握使用

    Java 認(rèn)識(shí)異常并掌握使用

    所謂異常是指程序在運(yùn)行時(shí)出現(xiàn)錯(cuò)誤時(shí)提示調(diào)用者的機(jī)制,異常的種類有很多,不同種類的異常有不同的含義,也有不同的處理方式,通讀本篇對(duì)大家的學(xué)習(xí)或工作具有一定的價(jià)值,需要的朋友可以參考下
    2021-09-09
  • SpringBoot使用Mybatis-Generator配置過(guò)程詳解

    SpringBoot使用Mybatis-Generator配置過(guò)程詳解

    這篇文章主要介紹了SpringBoot使用Mybatis-Generator配置過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-02-02
  • Java基于socket實(shí)現(xiàn)的客戶端和服務(wù)端通信功能完整實(shí)例

    Java基于socket實(shí)現(xiàn)的客戶端和服務(wù)端通信功能完整實(shí)例

    這篇文章主要介紹了Java基于socket實(shí)現(xiàn)的客戶端和服務(wù)端通信功能,結(jié)合完整實(shí)例形式分析了Java使用socket建立客戶端與服務(wù)器端連接與通信功能,需要的朋友可以參考下
    2018-05-05
  • 分析Java中為什么String不可變

    分析Java中為什么String不可變

    Java中為什么String是不可變性的。今天我們從多角度解析為什么Java把String做成不可變的。
    2021-06-06
  • Java Swing JTextField文本框的代碼示例

    Java Swing JTextField文本框的代碼示例

    這篇文章主要介紹了Java Swing JTextField文本框的代碼示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12
  • 基于java實(shí)現(xiàn)斗地主代碼實(shí)例解析

    基于java實(shí)現(xiàn)斗地主代碼實(shí)例解析

    這篇文章主要介紹了基于java實(shí)現(xiàn)斗地主代碼實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-07-07
  • Java枚舉(enum) 詳解7種常見(jiàn)的用法

    Java枚舉(enum) 詳解7種常見(jiàn)的用法

    這篇文章主要介紹了Java枚舉(enum) 詳解7種常見(jiàn)的用法,具有一定的參考價(jià)值,有需要的可以了解一下。
    2016-11-11
  • 詳解SpringBoot優(yōu)雅編碼之Lombok加持

    詳解SpringBoot優(yōu)雅編碼之Lombok加持

    這篇文章主要介紹了詳解SpringBoot優(yōu)雅編碼之Lombok加持,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-06-06
  • 如何將JSP/Servlet項(xiàng)目轉(zhuǎn)換為Spring Boot項(xiàng)目

    如何將JSP/Servlet項(xiàng)目轉(zhuǎn)換為Spring Boot項(xiàng)目

    這篇文章主要介紹了如何將JSP/Servlet項(xiàng)目轉(zhuǎn)換為Spring Boot項(xiàng)目,幫助大家更好的利用springboot進(jìn)行網(wǎng)絡(luò)編程,感興趣的朋友可以了解下
    2020-10-10

最新評(píng)論