前言
上一篇bean
加載的文章分析了bean
加載核心入口AbstractBeanFactory#doGetBean(String, Class, Object[], boolean)
的上半部分舵变,該部分主要邏輯在顯式調(diào)用getBean
時會被執(zhí)行,更具體的來說是獲得單例對象和通過自定義FactoryBean
創(chuàng)建對象時會執(zhí)行上半部分兜蠕;而下半部分會在初始化和獲得prototype
多例對象時被執(zhí)行挽懦,本文就是對這下半部分做深入分析
為了分析方便,我們再截取一次下半部分內(nèi)容的代碼舆声,代碼清單1
// (1)
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// (2)
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (args != null) {
// Delegation to parent with explicit args.
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
}
// (3)
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
try {
// (4)
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// (5)
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dependsOnBean : dependsOn) {
getBean(dependsOnBean);
registerDependentBean(dependsOnBean, beanName);
}
}
// (6)
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
public Object getObject() throws BeansException {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// (7)
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
// (8)
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
public Object getObject() throws BeansException {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; " +
"consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
我們都知道對于scope = "prototype"
的對象來說,Spring是不會在真正使用該對象前創(chuàng)建它的腋粥,這一特性意味著Spring不能解決prototype
類型變量的循環(huán)依賴問題,道理很簡單展辞,當(dāng)我們真正使用prototype
對象時肯定要求內(nèi)部的依賴關(guān)系都建立完畢罗珍,而循環(huán)依賴的存在使這種對象間關(guān)系無法建立覆旱,必定就會拋出BeanCurrentlyInCreationException
上一篇文中說過,Spring使用“三級緩存”的形式能部分解決循環(huán)依賴問題噪沙,我們在這里就可以總結(jié)下“部分”指哪些情況:Spring能夠解決singleton
下使用setter
方式形成的循環(huán)依賴問題,不能夠解決singleton
下使用構(gòu)造器形成的循環(huán)依賴朋腋,以及prototype
下的循環(huán)依賴。第一種能解決的原因是Spring解析之IoC:bean的加載(一)中分析過的緩存earlySingletonObjects
穷绵,當(dāng)我們設(shè)置允許早期對象引用暴露allowEarlyReference
后,Spring會將通過構(gòu)造器創(chuàng)建出來的目养,尚未調(diào)用setter
建立依賴關(guān)系的單例對象放入earlySingletonObjects
中,當(dāng)我們需要一組存在循環(huán)依賴的對象時直接從里面取出每個對象手動建立依賴關(guān)系即可努释。但是如果是構(gòu)造器形成的循環(huán)依賴伐蒂,連創(chuàng)建早期對象都不可能,自然就沒法解決循環(huán)依賴問題了
重溫了Spring解決循環(huán)依賴的手段后看標(biāo)注1缕减,如果獲取的是prototype
類型的對象且該對象正在創(chuàng)建中烛卧,必然就發(fā)生了循環(huán)依賴,直接拋出異常局雄。標(biāo)注2是存在父子容器時加載bean
的代碼邏輯蜈漓,如果大家用過SpringMVC相信對父子容器都不陌生融虽,大多數(shù)情況下Web層的SpringMVC都作為Service層和Dao層Spring容器的子容器存在,子容器可以訪問父容器中的bean
巍佑,反過來則不行萤衰,當(dāng)子容器加載bean
時首先判斷自身容器中是否存在同名的bean
,存在直接獲得筹吐,不存在就去父容器中查找該名稱的bean
標(biāo)注2的邏輯就是這樣丘薛,判斷如果存在父容器,且當(dāng)前容器中不存在beanName
對應(yīng)的bean
就調(diào)用父容器parentBeanFactory
的getBean(String, Object...)
希坚,又來了一個大循環(huán)裁僧。標(biāo)注3中typeCheckOnly
表示獲得bean
的目的是否是為了類型檢查,而不是真正使用這個bean
获洲,絕大部分情況我們當(dāng)時是要用bean
啦贡珊,這時就要調(diào)用markBeanAsCreated(String)
將創(chuàng)建bean
的行為做記錄爱致,記錄實際上就是將beanName
打上已經(jīng)創(chuàng)建的標(biāo)識放入Map<String, Boolean> alreadyCreated
中
標(biāo)注4在Spring解析之IoC:bean的加載(一)中已經(jīng)說過蒜鸡,如果<bean>
存在父子關(guān)系(注意不是容器的父子關(guān)系)叶沛,getMergedLocalBeanDefinition(String)
會將父子關(guān)系的<bean>
信息融合在RootBeanDefinition
中
checkMergedBeanDefinition(RootBeanDefinition, String, Object[])
排除了兩種情況下bean
的創(chuàng)建:1. <bean>
存在abstract
屬性判帮;2. scope = singleton
且參數(shù)args
有值的情況。第一種情況很好理解晌畅,都抽象了還創(chuàng)建毛線啊抗楔,而要想通第二種情況我們需要追根溯源看看args
來自哪里。顯式調(diào)用getBean
獲得對象有一個重載方法Object getBean(String, Object...)
入热,這里的args
就是第二個參數(shù)勺良,該重載方法提供的目的是解決一個場景:初始化只存在有參構(gòu)造的類,且參數(shù)要在獲取時動態(tài)指定尾组。創(chuàng)建只存在有參構(gòu)造器的類很容易讳侨,直接<constrcut-arg>
指定就好潮峦,但是要每次創(chuàng)建的參數(shù)值不同再用該方法很明顯就掛了啊忱嘹,還是Object getBean(String, Object...)
好使。這也很好的解釋了為什么args
不能和singleton
共存础米,因為args
說明可變性屁桑,singleton
說明唯一性,相互沖突
標(biāo)注5涉及到一個之前沒有講過的<bean>
屬性depends-on
乌叶,該屬性的作用是讓depends-on
內(nèi)的對象先于<bean>
所代表的對象創(chuàng)建准浴,但這兩組對象并不要求有真正的依賴關(guān)系乐横,我們舉個例子,創(chuàng)建三個類Man
催什、Woman
和Family
蒲凶,Family
配置depends-on
前兩個類
注意
Family
和Man
、Woman
只是組合關(guān)系并沒有形成依賴搀矫,在XML進行配置如下如果
depends-on
多個對象,多個對象之間可以用,
隔開,運行結(jié)果如下很明顯Spring先初始化了
depends-on
的對象隅要,至于多個depends-on
對象創(chuàng)建之間的順序和XML中對應(yīng)<bean>
書寫順序有關(guān)和depends-on
中的順序無關(guān),其實就是Spring自上而下解析標(biāo)簽的順序虏肾。讓我們再回到代碼清單1谴轮,標(biāo)注6第步、7、8三處很明顯根據(jù)scope
的不同將處理邏輯分成了三塊翩隧,為了分析清楚我們將每一塊單獨拎出來scope = singleton
對象的處理也分為三部分专缠,1藤肢、2兩部分是互有關(guān)聯(lián)的,Object getSingleton(String, ObjectFactory)
第二個參數(shù)是接口ObjectFactory
的匿名實現(xiàn)最住,實現(xiàn)了Object getObject()
方法涨缚,具體的實現(xiàn)又調(diào)用了Object createBean(String, RootBeanDefinition, Object[])
,而該方法又是一個模板茂翔,真正的具體實現(xiàn)在AbstractAutowireCapableBeanFactory
中珊燎,我們先走進標(biāo)注1看看做了什么首先從緩存
singletonObejcts
中獲取該單例對象,不存在進入創(chuàng)建流程谋国,beforeSingletonCreation(String)
做創(chuàng)建對象前的處理工作,之后調(diào)用匿名實現(xiàn)的getObject()
進而調(diào)用上面說的模板方法createBean
創(chuàng)建對象旅急,afterSingletonCreate(String)
做一些后處理操作藐吮,最后addSingleton(String, Object)
將創(chuàng)建的單例對象放入緩存isCreationCheckExclusions
保存在創(chuàng)建時不需要做校驗的bean
名稱迫摔,singletonCurrentlyInCreation
大家應(yīng)該很熟悉了,保存正在創(chuàng)建過程中的對象纱烘,整體邏輯就是擂啥,如果對象在創(chuàng)建時需要做校驗(說明還沒真正創(chuàng)建)哺壶,但在正在創(chuàng)建對象的容器中又有它,那就說明有問題塌碌,拋出BeanCurrentlyInCreationException
胖翰。同時這一步也讓大家知道了用于檢測循環(huán)依賴的singletonCurrentlyInCreation
是什么時候被塞入內(nèi)容的分析了這么多還在外圍轉(zhuǎn)悠萨咳,下面的
singletonFactory.getObject()
是創(chuàng)建bean
的核心代碼了吧培他?是也不是,是是因為核心創(chuàng)建流程確實在該方法中猛遍,不是是因為小小的方法里面涉及的東東那多的啊梯醒,Spring的東西果然浩瀚如海啊茸习。正因為這個問題的存在我想還是將核心邏輯再開一篇文章單獨分析吧号胚,要不然這篇文章得寫多少啊,讀者傷心寫者流淚啊杜漠。我們現(xiàn)在只需知道singletonFactory.getObject()
主要得到的對象就兩種:1.和<bean>
對應(yīng)的真實bean
驾茴;2.創(chuàng)建bean
的自定義FactoryBean
實例锈至,本篇文章先將外圍邏輯都清理干凈afterSingletonCreation(String)
閉著眼睛想都知道是創(chuàng)建單例之后的處理邏輯,和beforeSingletonCreation(String)
唯一不同的在于们拙,后者是將正在創(chuàng)建的對象放入singletonsCurrentlyInCreation
砚婆,而前者是創(chuàng)建完對象后從singletonsCurrentlyInCreation
移除。addSingleton(String, Object)
邏輯也很簡單在Spring解析之IoC:bean的加載(一)中提到部分解決循環(huán)依賴的“三級緩存”埂奈,也說到過數(shù)據(jù)只能存在其中一個緩存中海蔽,這里就是當(dāng)對象創(chuàng)建完成后將對象放入“一級”緩存中并刪除其余緩存中的該對象流程党窜,并在
registeredSingletons
已注冊對象HashSet
中保存對應(yīng)的beanName
回到圖7幌衣,標(biāo)注1、2都分析過了楚里,標(biāo)注3更好說了班缎,同樣在Spring解析之IoC:bean的加載(一)中已經(jīng)進行了詳細的分析达址,如果生成對象為
bean
直接返回,如果是自定義FactoryBean
满葛,調(diào)用其實現(xiàn)的getObject()
創(chuàng)建bean
后返回嘀韧,單例創(chuàng)建對象分析完畢,開始多例對象創(chuàng)建分析從宏觀上看
scope = prototype
處理流程和單例時一樣,圍繞createBean(String, RootBeanDefinition, Object[])
進行前后校驗處理十嘿,最后將可能的FactoryBean
轉(zhuǎn)成特定的bean
返回prototypesCurrentlyInCreation
是一個ThreadLocal<Object>
變量,其中保存了當(dāng)前線程正在創(chuàng)建的所有多例對象咳燕,只存在一個多例對象時ThreadLocal
內(nèi)存的就是字符串beanName
低缩,當(dāng)有多個時內(nèi)部存儲的就是HashSet
集合咆繁,這里的存儲又和本文最開始用prototypesCurrentlyInCreation
做多例類型循環(huán)依賴的判斷對應(yīng)上了玩般。createBean(String, RootBeanDefinition, Object[])
和上面一樣暫時跳過,afterPrototypeCreation(String)
思路和單例的后處理相似久脯,將創(chuàng)建好的多例對象從prototypesCurrentlyInCreation
中移除帘撰,最后一步getObjectForBeanInstance(Object, String, String, RootBeanDefinition)
和之前分析的一模一樣摧找,不再贅述。最后一組是剩下所有scope
對象的處理邏輯综苔,除了我們最常用的singleton
和prototype
外如筛,針對Web
項目Spring又提供了request
杨刨、session
芥颈、global session
等其他類型(不同Spring版本scope
也不一樣爬坑,到時大家看到多幾個少幾個不用詫異)妇垢,此外我們還可以實現(xiàn)Scope
接口創(chuàng)建自定義的scope
,這里給一篇文章上面有Spring3.0相關(guān)scope
類型的用法講解涨薪,大家可以拿來耍耍Bean scopes其實其他
scope
類型創(chuàng)建bean
的邏輯從上圖看和scope = “prototype”
相似刚夺,先從RootBeanDefinition
中得到配置的scope
,然后從Map<String, Scope> scopes
中得到該scope
對應(yīng)的處理類莽红,根據(jù)處理類中實現(xiàn)的Object get(String, ObjectFactory<?>)
安吁,如果實現(xiàn)的邏輯中調(diào)用了ObjectFactory
的Object getObject()
鬼店,那就又回到了多例的處理邏輯,剩下的大家看前面的分析即可
后記
本文將getBean
中最后零碎的邏輯清理干凈就是為了將核心創(chuàng)建bean
作為一個整體分析巍棱,即便如此由于Spring涉及的內(nèi)容太多拉盾,createBean
依然比較龐大和雜亂捉偏,不管怎么說我們已經(jīng)吹響了最后的沖鋒號夭禽,年前必定攻下獲得bean
這座山頭讹躯,加油!