眾所周知,spring託管了我們物件的建立,銷毀,管理著整個bean的生命週期。但是在物件的建立過程中,有一種特殊情況,存在可能兩個bean之間互相引用,例如下面的testa中引用了testb,testa中引用了testa,即你中有我,我中有你。
public class testa
******************************==
public class testb
那像這種情況,為什麼會產生迴圈依賴呢,spring又是如何巧妙的解決了這麼乙個問題呢?我們帶著問題繼續看下去。
在spring中,所有的bean預設是單例的,即singleton。而多例模式下的迴圈依賴,spring是無法解決的。首先看下原始碼,這裡有乙個prototypescurrentlyincreation變數,很是重要,spring也給出了說明,這個變數用來記錄當前正在建立的beanname。最後再著重介紹下到底為什麼要設這麼乙個標誌。
/** names of beans that are currently in creation */
private final threadlocalprototypescurrentlyincreation = new namedthreadlocal<>("prototype beans currently in creation");
在剛開始呼叫dogetbean(beanname)來建立bean的時候,會呼叫isprototypecurrentlyincreation方法進行判斷,判斷當前的beanname是否在當前執行緒變數中,如果在則直接丟擲beancurrentlyincreationexception異常。
/*在剛進來建立時,會判斷當前bean是否正在建立*/
if (isprototypecurrentlyincreation(beanname))
protected boolean isprototypecurrentlyincreation(string beanname)
第一次進來建立時,變數中肯定沒有儲存,返回false。接著往下走,呼叫beforeprototypecreation方法,將當前的beanname加入當前執行緒的變數中儲存起來,打上乙個標記,當前的bean已經開始建立了,為以後的迴圈依賴做準備。
//ioc容器建立原型模式bean例項物件
else if (mbd.isprototype())
finally
//獲取給定bean的例項物件
bean = getobjectforbeaninstance(prototypeinstance, name, beanname, mbd);
}protected void beforeprototypecreation(string beanname)
//...省略無關**
}
第二步會呼叫createbean方法,而cratebean的真正是實現是docreatebean,這裡會完成bean的建立,屬性依賴注入,初始化等一系列操作。
//建立bean例項物件
@override
protected object createbean(string beanname, rootbeandefinition mbd, @nullable object args)
throws beancreationexception
catch (beancreationexception ex)
//...省略無關**
}//真正建立bean的方法
protected object docreatebean(final string beanname, final rootbeandefinition mbd, final @nullable object args)
throws beancreationexception
//bean物件的初始化,依賴注入在此觸發
//初始化bean物件
exposedobject = initializebean(beanname, exposedobject, mbd);
//...省略無關**
return exposedobject;
}
在呼叫populatebean方法時,會去為每個屬性賦值,以上面的testa,testb為例,當testa發現依賴testb,接著就會去getbean(testb),testb的建立流程跟testa一樣。首先會先去set集合中獲取當前beanname是否正在建立當中,如果沒有,會進行set操作,儲存到當前執行緒變數中。然後繼續進行bean的建立。當呼叫到populatebean方法時,testb會發現依賴testa,那麼轉過身又會回去建立testa,當呼叫到isprototypecurrentlyincreation方法時,此時的testa由於還在進行屬性賦值,並沒有完全的建立成功,那麼理所當然他會在set集合當中,此時程式會直接丟擲異常,告知當前的bean正在建立當中。
那麼問題來了,如果不拋異常,會怎麼樣呢?沒有這麼乙個set集合又會怎麼樣呢?大家可以試想一下。如果沒有這麼乙個標記,testa在進行屬性賦值時,發現依賴testb,馬上又去建立testb,到testb進行屬性賦值時,又發現依賴了testa,轉過頭又去建立testa,而testa依賴了testb,則又會去建立testb。迴圈往復,形成了乙個死迴圈,永遠都出不來了。所以spring問了避免這種現象,索性直接丟擲異常,因為在spring看來,它也不知道如何來解決這種情況。
Spring原始碼解析之 Aop原始碼解析(2)
spring aop 更多的是oop開發模式的乙個補充,幫助oop以更好的方式來解決對於需要解決業務功能模組之上統一管理 的功能 以一副圖來做為aop功能的說明更直觀些。對於類似系統的安全檢查,系統日誌,事務管理等相關功能,物件導向的開發方法並沒有更好的解決方法 aop引入了一些概念。更多的是spr...
Spring原始碼之XML解析
資料準備階段 準備的目的是封裝 resource引數,目的是為了考慮到 resource可能存在編碼要求的情況,其次,通過 sax讀取 xml檔案的方式來準備 inputsource物件,最後將引數傳遞到 最核心的實現部分 doloadbeandefinitions inputsource,enco...
Spring 原始碼解析之BeanFactory介面
beanfactory介面是spring 容器的根介面,其他介面和類通過對這個介面的實現物件的例項化,通過對該介面的控制,實現對目標物件的例項化。string factory bean prefix 用於取消對beanfactory例項的引用區分factory和其實例,如果是 返回factory,否...