摘要:如下圖注意,這里不是函數(shù)的循環(huán)調(diào)用,是對(duì)象的相互依賴關(guān)系。因此如果在創(chuàng)建過程中發(fā)現(xiàn)自己已經(jīng)在當(dāng)前創(chuàng)建池里時(shí)將拋出異常表示循環(huán)依賴而對(duì)于創(chuàng)建完畢的將從當(dāng)前創(chuàng)建池中清除掉。
什么是循環(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)依賴場景有:
構(gòu)造器的循環(huán)依賴
setter方式單例,默認(rèn)方式
setter方式原型,prototype
第一種:構(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池”中清除掉。
首先我們先初始化三個(gè)Bean。
OK,上面是很基本的3個(gè)類,,StudentA有參構(gòu)造是StudentB。StudentB的有參構(gòu)造是StudentC,StudentC的有參構(gòu)造是StudentA ,這樣就產(chǎn)生了一個(gè)循環(huán)依賴的情況,
我們都把這三個(gè)Bean交給Spring管理,并用有參構(gòu)造實(shí)例化。
下面是測試類:
publicclassTest{
publicstaticvoidmain(String[] args){
ApplicationContext context =newClassPathXmlApplicationContext("com/zfx/student/applicationContext.xml");
//System.out.println(context.getBean("a", StudentA.class));
}
}
執(zhí)行結(jié)果報(bào)錯(cuò)信息為:
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating beanwithname"a": Requested beaniscurrentlyincreation:Isthere an unresolvable circular reference?
如果大家理解開頭那句話的話,這個(gè)報(bào)錯(cuò)應(yīng)該不驚訝,Spring容器先創(chuàng)建單例StudentA,StudentA依賴StudentB,然后將A放在“當(dāng)前創(chuàng)建Bean池”中,此時(shí)創(chuàng)建StudentB,StudentB依賴StudentC ,然后將B放在“當(dāng)前創(chuàng)建Bean池”中,此時(shí)創(chuàng)建StudentC,StudentC又依賴StudentA, 但是,此時(shí)Student已經(jīng)在池中,所以會(huì)報(bào)錯(cuò),,因?yàn)樵诔刂械腂ean都是未初始化完的,所以會(huì)依賴錯(cuò)誤 ,(初始化完的Bean會(huì)從池中移除)
第二種:setter方式單例,默認(rèn)方式
如果要說setter方式注入的話,我們最好先看一張Spring中Bean實(shí)例化的圖
如圖中前兩步驟得知:Spring是先將Bean對(duì)象實(shí)例化之后再設(shè)置對(duì)象屬性的
修改配置文件為set方式注入
下面是測試類:
publicclassTest{
publicstaticvoidmain(String[] args){
ApplicationContext context =newClassPathXmlApplicationContext("com/zfx/student/applicationContext.xml");
System.out.println(context.getBean("a", StudentA.class));
}
}
打印結(jié)果為:
com.zfx.student.StudentA@1fbfd6
為什么用set方式就不報(bào)錯(cuò)了呢 ?
我們結(jié)合上面那張圖看, Spring先是用構(gòu)造實(shí)例化 Bean對(duì)象 ,此時(shí)Spring會(huì)將這個(gè)實(shí)例化結(jié)束的對(duì)象放到一個(gè)Map中,并且Spring提供了獲取這個(gè)未設(shè)置屬性的實(shí)例化對(duì)象引用的方法。
結(jié)合我們的實(shí)例來看,,當(dāng)Spring實(shí)例化了StudentA、StudentB、StudentC后,緊接著會(huì)去設(shè)置對(duì)象的屬性,此時(shí)StudentA依賴StudentB,就會(huì)去Map中取出存在里面的單例StudentB對(duì)象,以此類推,不會(huì)出來循環(huán)的問題嘍、
下面是Spring源碼中的實(shí)現(xiàn)方法。以下的源碼在Spring的Bean包中的DefaultSingletonBeanRegistry.java類中
第三種:setter方式原型,prototype
修改配置文件為:
測試用例:
publicclassTest{
publicstaticvoidmain(String[] args){
ApplicationContext context =newClassPathXmlApplicationContext("com/zfx/student/applicationContext.xml");
//此時(shí)必須要獲取Spring管理的實(shí)例,因?yàn)楝F(xiàn)在scope="prototype" 只有請(qǐng)求獲取的時(shí)候才會(huì)實(shí)例化對(duì)象
System.out.println(context.getBean("a", StudentA.class));
}
}
打印結(jié)果:
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating beanwithname"a": Requested beaniscurrentlyincreation:Isthere an unresolvable circular reference?
為什么原型模式就報(bào)錯(cuò)了呢 ?
對(duì)于“prototype”作用域Bean,Spring容器無法完成依賴注入,因?yàn)椤皃rototype”作用域的Bean,Spring容器不進(jìn)行緩存,因此無法提前暴露一個(gè)創(chuàng)建中的Bean。
感謝您耐心看完的文章
順便給大家推薦一個(gè)Java技術(shù)交流群:710373545里面會(huì)分享一些資深架構(gòu)師錄制的視頻資料:有Spring,MyBatis,Netty源碼分析,高并發(fā)、高性能、分布式、微服務(wù)架構(gòu)的原理,JVM性能優(yōu)化、分布式架構(gòu)等這些成為架構(gòu)師必備的知識(shí)體系。還能領(lǐng)取免費(fèi)的學(xué)習(xí)資源,目前受益良多!
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/74638.html
摘要:當(dāng)普通對(duì)象要轉(zhuǎn)換成時(shí)就很有用,因?yàn)榉祷氐母袷脚c構(gòu)造函數(shù)接受的格式完全相同。使用常規(guī)的構(gòu)造函數(shù)可以將一個(gè)二維鍵值對(duì)數(shù)組轉(zhuǎn)換成一個(gè)對(duì)象。在和早期標(biāo)準(zhǔn)中,根本沒有指定屬性的順序。此函數(shù)還可以輕松地將純對(duì)象屬性映射到對(duì)象中。 為了保證的可讀性,本文采用意譯而非直譯。 想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等著你! 自身可枚舉屬性 Object.keys() 方法會(huì)返回一個(gè)...
摘要:雖然和都可以獲取組件實(shí)例,但是它們無法在跨級(jí)或兄弟間通信,這是它們的缺點(diǎn)。也就是在父組件中提供一個(gè)值,并且在需要使用的子孫組件中注入改值,即不僅僅是,只要是的子組件,無論隔多少代,都可以通過這個(gè)的方式注入。通過混入組件,實(shí)現(xiàn)組件間的通信。 寫在前面 vue 的組件化應(yīng)該是其最核心的思想了,無論是一個(gè)大的頁面還是一個(gè)小的按鈕,都可以被稱之為組件?;?Vue 的開發(fā),就是在寫一個(gè)個(gè)組件,...
摘要:本教程解釋了現(xiàn)代中各種各樣的循環(huán)可能性目錄介紹提供了許多迭代循環(huán)的方法。引入了循環(huán),它結(jié)合了的簡潔性和破解能力注意使用。此循環(huán)在每次迭代中創(chuàng)建一個(gè)新范圍,因此我們可以安全地使用它而不是。 JavaScript提供了許多通過LOOPS迭代的方法。本教程解釋了現(xiàn)代JAVASCRIPT中各種各樣的循環(huán)可能性 showImg(https://segmentfault.com/img/bVbfH...
摘要:本教程解釋了現(xiàn)代中各種各樣的循環(huán)可能性目錄介紹提供了許多迭代循環(huán)的方法。引入了循環(huán),它結(jié)合了的簡潔性和破解能力注意使用。此循環(huán)在每次迭代中創(chuàng)建一個(gè)新范圍,因此我們可以安全地使用它而不是。 JavaScript提供了許多通過LOOPS迭代的方法。本教程解釋了現(xiàn)代JAVASCRIPT中各種各樣的循環(huán)可能性 showImg(https://segmentfault.com/img/bVbfH...
摘要:本教程解釋了現(xiàn)代中各種各樣的循環(huán)可能性目錄介紹提供了許多迭代循環(huán)的方法。引入了循環(huán),它結(jié)合了的簡潔性和破解能力注意使用。此循環(huán)在每次迭代中創(chuàng)建一個(gè)新范圍,因此我們可以安全地使用它而不是。 JavaScript提供了許多通過LOOPS迭代的方法。本教程解釋了現(xiàn)代JAVASCRIPT中各種各樣的循環(huán)可能性 showImg(https://segmentfault.com/img/bVbfH...
閱讀 3698·2021-09-07 10:19
閱讀 3639·2021-09-03 10:42
閱讀 3592·2021-09-03 10:28
閱讀 2560·2019-08-29 14:11
閱讀 819·2019-08-29 13:54
閱讀 1605·2019-08-29 12:14
閱讀 426·2019-08-26 12:12
閱讀 3624·2019-08-26 10:45