循環(huán)依賴是指兩個或者多個Bean之前相互持有對方。在Spring中循環(huán)依賴一般有三種方式:
構(gòu)造函數(shù)循環(huán)依賴
setter方法循環(huán)依賴
prototype 范圍的依賴處理
在Spring中構(gòu)造函數(shù)循環(huán)依賴是無法解決的,因為構(gòu)造函數(shù)依賴其實是方法間循環(huán)調(diào)用的一種,會發(fā)生死循環(huán)。但是在Spring中會直接拋出BeanCurrentlyInCreationException
異常。源碼如下:
//?在緩存中獲取Bean,如果沒有就創(chuàng)建Beanpublic?Object?getSingleton(String?beanName,?ObjectFactory<?>?singletonFactory)?{ Assert.notNull(beanName,?"'beanName'?must?not?be?null"); synchronized?(this.singletonObjects)?{ //?在緩存中獲取Bean Object?singletonObject?=?this.singletonObjects.get(beanName); if?(singletonObject?==?null)?{ //?判斷容器是否正在銷毀單實例Bean if?(this.singletonsCurrentlyInDestruction)?{ throw?new?BeanCreationNotAllowedException(beanName, "Singleton?bean?creation?not?allowed?while?singletons?of?this?factory?are?in?destruction?"?+ "(Do?not?request?a?bean?from?a?BeanFactory?in?a?destroy?method?implementation!)"); } //?將當前需要創(chuàng)建的Bean標示放到Set集合,如果失敗則拋出BeanCurrentlyInCreationException異常 beforeSingletonCreation(beanName); boolean?newSingleton?=?false; boolean?recordSuppressedExceptions?=?(this.suppressedExceptions?==?null); if?(recordSuppressedExceptions)?{ this.suppressedExceptions?=?new?LinkedHashSet<Exception>(); } try?{ //?創(chuàng)建Bean實例 singletonObject?=?singletonFactory.getObject(); newSingleton?=?true; } ... if?(newSingleton)?{ //?將Bean實例注冊到singletonObjects容器中 addSingleton(beanName,?singletonObject); } } return?(singletonObject?!=?NULL_OBJECT???singletonObject?:?null); } }protected?void?beforeSingletonCreation(String?beanName)?{ //?將當前需要創(chuàng)建的Bean標示方法Set集合,如果失敗則拋出BeanCurrentlyInCreationException異常 if?(!this.inCreationCheckExclusions.contains(beanName)?&&?!this.singletonsCurrentlyInCreation.add(beanName))?{ throw?new?BeanCurrentlyInCreationException(beanName); } }
執(zhí)行過程:
從緩存中獲取Bean,如果沒有則走創(chuàng)建Bean流程
判斷容器是否正在銷毀單實例Bean,如果是則不創(chuàng)建Bean
將當前需要創(chuàng)建的Bean標示(name)放入Set集合中(當前正在創(chuàng)建的Bean池),如果放入失敗則拋出BeanCurrentlyInCreationException
異常
創(chuàng)建Bean實例
將Bean實例注冊到容器(放到緩存中)
解決構(gòu)造函數(shù)依賴主要是第3步實現(xiàn)的,Spring在容器創(chuàng)建的Bean的時候,會將Bean的標示(name)放到一個Set集合里面(當前正在創(chuàng)建的Bean池)。當在創(chuàng)建Bean的過程中,發(fā)現(xiàn)自已經(jīng)在這個Set集合中時,就直接會拋出
BeanCurrentlyInCreationException
異常,而不會發(fā)生死循環(huán)。
@Servicepublic?class?AService?{????@Autowired ????private?BService?bService;????@Autowired ????public?void?setbService(BService?bService)?{????????this.bService?=?bService; ????} }
這兩種方式都算是setter方法依賴。我們創(chuàng)建單實例Bean的大致過程可以劃分成三個階段:
實例化?createBeanInstance(beanName, mbd, args);
設(shè)置屬性值?populateBean(beanName, mbd, instanceWrapper);
初始化?initializeBean(beanName, exposedObject, mbd);
對于Setter注入造成的循環(huán)依賴,Spring容器是在創(chuàng)建Bean第一步實例化后,就將Bean的引用提前暴露出來。通過提前暴露出一個單例工廠方法,從而使得其他Bean可以引用到該Bean。
創(chuàng)建Bean時提前暴露剛完成第一步的Bean,源碼如下:
addSingletonFactory(beanName,?new?ObjectFactory<Object>()?{ @Override public?Object?getObject()?throws?BeansException?{ return?getEarlyBeanReference(beanName,?mbd,?bean); } });//?將單例工廠放入緩存中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); } } }
自動裝配過程中獲取單實例Bean,源碼如下:
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); }
我們可以看到,在自動裝配Bean的過程中,會去找三個緩存:
singletonObjects:存放完成創(chuàng)建的Bean所有步驟的單實例Bean
earlySingletonObjects:存放只完成了創(chuàng)建Bean的第一步,且是由單實例工廠創(chuàng)建的Bean
singletonFactories:存放只完成了創(chuàng)建Bean的第一步后,提前暴露Bean的單實例工廠。http://www.chacha8.cn/detail/1132398214.html
這里為什么會用一個三級緩存呢,為啥不直接將只完成了創(chuàng)建Bean的第一步的Bean直接方到
earlySingletonObjects
緩存中呢?我們跟入
?getEarlyBeanReference(beanName, mbd, bean)
我們可以發(fā)現(xiàn),單實例工廠方法返回Bean的時候還執(zhí)行了后置處理器的,在后置處理器中我們還可以對Bean進行一些特殊處理。如果我們直接將剛完成實例化的Bean放入earlySingletonObjects
緩存中,那么失去對Bean進行特殊處理的機會。
源碼如下:
protected?Object?getEarlyBeanReference(String?beanName,?RootBeanDefinition?mbd,?Object?bean)?{ Object?exposedObject?=?bean; if?(bean?!=?null?&&?!mbd.isSynthetic()?&&?hasInstantiationAwareBeanPostProcessors())?{ for?(BeanPostProcessor?bp?:?getBeanPostProcessors())?{ if?(bp?instanceof?SmartInstantiationAwareBeanPostProcessor)?{ SmartInstantiationAwareBeanPostProcessor?ibp?=?(SmartInstantiationAwareBeanPostProcessor)?bp; exposedObject?=?ibp.getEarlyBeanReference(exposedObject,?beanName); if?(exposedObject?==?null)?{ return?null; } } } } return?exposedObject; }
對于
singleton?
作用域 bean ,可以通過setAllowCircularReferences(false);
來禁用循環(huán)引用。鄭州不育不孕醫(yī)院:http://www.zzchyy110.com/
對于prototype?
作用域 bean, Spring 容器無法完成依賴注入,因為 Spring 容器不進行緩存prototype
作用域的 bean ,因此無法提前暴露一個創(chuàng)建中的 bean 示。
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)cdcxhl.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機、免備案服務(wù)器”等云主機租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。
本文名稱:Spring源碼(八)循環(huán)依賴-創(chuàng)新互聯(lián)
分享地址:http://www.rwnh.cn/article44/dcejhe.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供靜態(tài)網(wǎng)站、外貿(mào)建站、網(wǎng)頁設(shè)計公司、網(wǎng)站排名、面包屑導(dǎo)航、ChatGPT
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容