讀完這篇文章你將會收穫到web
Spring
循環依賴能夠分爲哪兩種
Spring
如何解決
setter
循環依賴
Spring
爲什麼是三級緩存 , 二級不行 ?
Spring
爲啥不能解決構造器循環依賴
循環依賴就是循環引用,兩個或以上的 bean
相互持有對方。好比說 beanA
引用 beanB
, beanB
引用 beanC
, beanC
引用 beanA
, 它們之間的引用關係構成一個環。緩存
Spring
中的循環依賴包括併發
setter
循環依賴
Spring
對於構造器的依賴、沒法解決。只會拋出 BeanCurrentlyInCreationException
異常。編輯器
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } } 複製代碼
不論是 autowireByName
仍是 autowireByType
都是屬於這種。Spring
默認是可以解決這種循環依賴的,主要是經過 Spring
容器提早暴露剛完成構造器注入但未完成其餘步驟的 bean 來完成的。並且只能解決 singleton
類型的循環依賴、對於 prototype
類型的是不支持的,由於 Spring
沒有緩存這種類型的 bean
函數
其實很簡單、在 Spring 獲取單例流程(一) 中咱們曾說起過三級緩存this
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) { Object singletonObject = this.singletonObjects.get(beanName); // 這個bean 正處於 建立階段 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { // 併發控制 synchronized (this.singletonObjects) { // 單例緩存是否存在 singletonObject = this.earlySingletonObjects.get(beanName); // 是否運行獲取 bean factory 建立出的 bean if (singletonObject == null && allowEarlyReference) { // 獲取緩存中的 ObjectFactory ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); // 將對象緩存到 earlySingletonObject中 this.earlySingletonObjects.put(beanName, singletonObject); // 從工廠緩衝中移除 this.singletonFactories.remove(beanName); } } } } return singletonObject; } 複製代碼
Spring
解決 setter
循環依賴的關鍵點就是在這裏,主要是 singletonFactories
這個 Map
中url
咱們能夠先梳理一下總體的流程spa
beanA --> beanB --> beanC -->beanA
複製代碼
以上面爲例子、咱們先假設它們是構造器的循環依賴prototype
Spring
初始化完成以後、接收到一個
getBean
的調用請求、請求
beanA
Spring
發現
三級緩存中都沒有
beanA
的存在、因此開始建立
beanA
的流程
beanA
放入到
singletonsCurrentlyInCreation
集合中去、表明着
beanA
正在建立中
new
一個
beanA
的對象、我要先得到一個
beanB
的對象、好、咱們就進行一個
getBean(beanB)
Spring
發現
三級緩存中都沒有
beanB
的存在、因此開始建立
beanB
的流程
beanB
放入到
singletonsCurrentlyInCreation
集合中去、表明着
beanB
正在建立中
new
一個
beanB
的對象、我要先得到一個
beanC
的對象、好、咱們就進行一個
getBean(beanC)
Spring
發現
三級緩存中都沒有
beanC
的存在、因此開始建立
beanC
的流程
beanC
放入到
singletonsCurrentlyInCreation
集合中去、表明着
beanC
正在建立中
new
一個
beanC
的對象、我要先得到一個
beanA
的對象、好、咱們就進行一個
getBean(beanA)
Spring
發現
三級緩存中都沒有
beanA
的存在、因此開始建立
beanA
的流程
beanA
放入到
singletonsCurrentlyInCreation
集合中去、可是在這個時候、插入到集合中失敗、直接拋出異常
而假如咱們是一個 setter
的循環依賴3d
Spring
初始化完成以後、接收到一個
getBean
的調用請求、請求
beanA
beanA
,若是沒有則往下進行
beanA
放入到
singletonsCurrentlyInCreation
集合中去、表明着
beanA
正在建立中
beanA
, 可是這個時候的
beanA
是一個不完整的狀態、由於不少屬性沒有被賦值、好比說
beanA
中的成員變量
beanB
如今仍是一個
null
的狀態
beanA
加入到
第三級緩存中,正常來講都是會被加入到
第三級緩存中的
beanB
對象
beanB
,若是沒有則建立一個並不完整的
beanB
、而後加入到
第三級緩存中、而後發現須要填充一個
beanC
的屬性
beanC
,若是沒有則建立一個並不完整的
beanC
、而後加入到
第三級緩存中、而後發現須要填充一個
beanA
的屬性
beanA
,發如今
第三級緩衝中有不完整的
beanA
、將其從
第三級緩存中移除出來、放入到
第二級緩存中,而後返回給
beanC
用於填充屬性
beanC
的 屬性填充完畢,則將其從
singletonsCurrentlyInCreation
集合中移除掉,表明
beanC
已經真正的建立好了
beanC
加入到
第一級緩存中,並將其從
第三級緩存中移除,並返回給
beanB
,
beanB
也如
beanC
那樣處理
beanA
也如
beanB
、
beanC
那樣處理、加入到
第一級緩存中、而後從
第二級緩存中移除
其實上面的屁話又長又臭,可是流程仍是很是簡單的
/** * Cache of singleton objects: bean name to bean instance. * 存放的是單例 bean、對應關係是 bean Name --> bean instance */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); /** * Cache of early singleton objects: bean name to bean instance. * 存放的早期的 bean、對應的關係 也是 beanName --> bean instance * 與 singletonObjects 區別在於 earlySingletonObjects 中存放的bean 不必定是完整的、 * bean 在建立過程當中就加入到 earlySingletonObjects 中了、因此在bean建立過程當中就能夠經過getBean 方法獲取、 * 這個Map 也是解決循環依賴的關鍵所在 **/ private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); /** * Cache of singleton factories: bean name to ObjectFactory. * 存放的是 ObjectFactory 、能夠理解爲建立單例bean的factory、對應關係是 bean name --> objectFactory */ private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); 複製代碼
咱們來看看從第三級緩存升級到第二級緩存究竟發生了什麼
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean; if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof SmartInstantiationAwareBeanPostProcessor) { SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp; exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName); } } } return exposedObject; } // 默認實現 default Object getEarlyBeanReference(Object bean, String beanName) throws BeansException { return bean; } 複製代碼
其實只要有二級緩存也是能夠的,雖然能夠達到解決 setter
循環依賴的問題、可是卻沒法給用戶提供一個擴展接口(當存在循環依賴的)。
就比如說、上面的例子、在循環依賴的關係中,當 beanA
從第三級緩存升級到第二級緩存的時候,咱們能夠在其升級的時候去設置一些 beanA
的屬性或者作一些其餘事情,咱們只須要在 beanA 的類中實現 SmartInstantiationAwareBeanPostProcessor
接口便可
可是單純只有二級緩存的話,當咱們建立好一個沒有完成初始化的 bean
的時候、要麼就直接調用 ObjectFactory
的 getObject
方法獲取通過回調的 bean
放入到第二級緩存(無論這個 bean
存不存在一個循環引用的關係鏈中),要麼就直接放剛剛建立好的沒有完成初始化的 bean
放入到第二級緩存。不管是哪一種狀況,都沒法達到這樣一個需求:當存在循環依賴的時候,咱們做爲用戶須要對其進行一些設置或者一些其餘的操做
若是按照解決 setter
循環依賴的流程、是否可以解決?先將一個不完整的 bean
放入到第三級緩存中,而後提供出去給其餘 bean
依賴。可是呢,問題是我沒法建立出這麼一個不完整的 bean
在一個構造函數依賴的關係中,參數不全,再牛皮也不能把
本文使用 mdnice 排版