您好,登錄后才能下訂單哦!
循環依賴是指兩個或者多個Bean之前相互持有對方。在Spring中循環依賴一般有三種方式:
構造函數循環依賴
setter方法循環依賴
prototype 范圍的依賴處理
在Spring中構造函數循環依賴是無法解決的,因為構造函數依賴其實是方法間循環調用的一種,會發生死循環。但是在Spring中會直接拋出BeanCurrentlyInCreationException
異常。源碼如下:
//?在緩存中獲取Bean,如果沒有就創建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!)"); } //?將當前需要創建的Bean標示放到Set集合,如果失敗則拋出BeanCurrentlyInCreationException異常 beforeSingletonCreation(beanName); boolean?newSingleton?=?false; boolean?recordSuppressedExceptions?=?(this.suppressedExceptions?==?null); if?(recordSuppressedExceptions)?{ this.suppressedExceptions?=?new?LinkedHashSet<Exception>(); } try?{ //?創建Bean實例 singletonObject?=?singletonFactory.getObject(); newSingleton?=?true; } ... if?(newSingleton)?{ //?將Bean實例注冊到singletonObjects容器中 addSingleton(beanName,?singletonObject); } } return?(singletonObject?!=?NULL_OBJECT???singletonObject?:?null); } }protected?void?beforeSingletonCreation(String?beanName)?{ //?將當前需要創建的Bean標示方法Set集合,如果失敗則拋出BeanCurrentlyInCreationException異常 if?(!this.inCreationCheckExclusions.contains(beanName)?&&?!this.singletonsCurrentlyInCreation.add(beanName))?{ throw?new?BeanCurrentlyInCreationException(beanName); } }
執行過程:
從緩存中獲取Bean,如果沒有則走創建Bean流程
判斷容器是否正在銷毀單實例Bean,如果是則不創建Bean
將當前需要創建的Bean標示(name)放入Set集合中(當前正在創建的Bean池),如果放入失敗則拋出BeanCurrentlyInCreationException
異常
創建Bean實例
將Bean實例注冊到容器(放到緩存中)
解決構造函數依賴主要是第3步實現的,Spring在容器創建的Bean的時候,會將Bean的標示(name)放到一個Set集合里面(當前正在創建的Bean池)。當在創建Bean的過程中,發現自已經在這個Set集合中時,就直接會拋出
BeanCurrentlyInCreationException
異常,而不會發生死循環。
@Servicepublic?class?AService?{????@Autowired ????private?BService?bService;????@Autowired ????public?void?setbService(BService?bService)?{????????this.bService?=?bService; ????} }
這兩種方式都算是setter方法依賴。我們創建單實例Bean的大致過程可以劃分成三個階段:
實例化?createBeanInstance(beanName, mbd, args);
設置屬性值?populateBean(beanName, mbd, instanceWrapper);
初始化?initializeBean(beanName, exposedObject, mbd);
對于Setter注入造成的循環依賴,Spring容器是在創建Bean第一步實例化后,就將Bean的引用提前暴露出來。通過提前暴露出一個單例工廠方法,從而使得其他Bean可以引用到該Bean。
創建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:存放完成創建的Bean所有步驟的單實例Bean
earlySingletonObjects:存放只完成了創建Bean的第一步,且是由單實例工廠創建的Bean
singletonFactories:存放只完成了創建Bean的第一步后,提前暴露Bean的單實例工廠。http://www.chacha8.cn/detail/1132398214.html
這里為什么會用一個三級緩存呢,為啥不直接將只完成了創建Bean的第一步的Bean直接方到
earlySingletonObjects
緩存中呢?我們跟入
?getEarlyBeanReference(beanName, mbd, bean)
我們可以發現,單實例工廠方法返回Bean的時候還執行了后置處理器的,在后置處理器中我們還可以對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);
來禁用循環引用。鄭州不育不孕醫院:http://www.zzchyy110.com/
對于prototype?
作用域 bean, Spring 容器無法完成依賴注入,因為 Spring 容器不進行緩存prototype
作用域的 bean ,因此無法提前暴露一個創建中的 bean 示。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。