Spring DI三部曲之實(shí)例化

什么是依賴注入?
依賴注入(Dependency Injection,DI)是我們可以用來實(shí)現(xiàn) IoC 的一種模式,其中被反轉(zhuǎn)的控制是設(shè)置對(duì)象的依賴關(guān)系。
將對(duì)象與其他對(duì)象連接起來,或?qū)?duì)象“注入”到其他對(duì)象中,是由程序完成的,而不是由對(duì)象本身完成的。
Spring中的DI
當(dāng)大家開始學(xué)習(xí)spring的時(shí)候,都會(huì)從如下的示例代碼開始,在前面幾篇文章中詳細(xì)講解了第一行代碼,spring是如何實(shí)現(xiàn)IoC的,現(xiàn)在我們要講解spring是如何實(shí)現(xiàn)DI的。

注意:本文是以5.2.3版本為講解。
步驟一:獲取bean 實(shí)例
返回指定 bean 的一個(gè)實(shí)例,該實(shí)例可以是共享的,也可以是獨(dú)立的。
此方法允許將 Spring BeanFactory 用作 Singleton 或 Prototype 設(shè)計(jì)模式的替代品。在 Singleton bean 的情況下,調(diào)用者可以保留對(duì)返回對(duì)象的引用。
該方法的具體實(shí)現(xiàn)是在“AbstractBeanFactory”類中實(shí)現(xiàn)的。

步驟二:獲取IoC容器中指定名稱的Bean實(shí)例
在這方法里調(diào)用“doGetBean”方法,這個(gè)方法才是真正向IoC容器獲取Bean對(duì)象

步驟三:獲取bean實(shí)例
這個(gè)方法代碼比較多,大致業(yè)務(wù)邏輯如下:
transformedBeanName:獲取bean 名稱,必要時(shí)去除工廠取消引用前綴“&”,并將別名解析為規(guī)范名稱
geetSingleton:獲取指定名稱的單例對(duì)象。檢查已經(jīng)實(shí)例化的單例,還允許對(duì)當(dāng)前創(chuàng)建的單例進(jìn)行早期引用(解決循環(huán)引用)
getObjectForBeanInstance:如果單例對(duì)象非空,獲取bean 實(shí)例的對(duì)象,bean 實(shí)例本身或其創(chuàng)建的對(duì)象(如果是 FactoryBean)
檢查當(dāng)前bean工廠是否包含該bean,如果沒有找到檢查父級(jí)容器中是否包含該bean,如果父級(jí)容器還找不到,使用顯式參數(shù)委托給父級(jí)容器查找
markBeanAsCreated:將指定的 bean 標(biāo)記為已創(chuàng)建(或即將創(chuàng)建)。這允許 bean 工廠優(yōu)化其緩存以重復(fù)創(chuàng)建指定的 bean。
getMergedLocalBeanDefinition:獲取一個(gè)RootBeanDefinition,如果指定的 bean 對(duì)應(yīng)于子 bean 定義,則遍歷父 bean 定義
checkMergedBeanDefinition:判斷RootBeanDefinition是抽象類,則拋異常
getDependsOn:獲取當(dāng)前Bean所依賴Bean的名稱,遞歸調(diào)用getBean方法進(jìn)行注冊(cè)
如果是單例模式(singleton),通過“getSingleton”方法獲取名稱注冊(cè)的(原始)單例對(duì)象,如果尚未注冊(cè),則通過“createBean”方法創(chuàng)建并注冊(cè)一個(gè)新對(duì)象
如果是原型模式(prototype),通過“beforePrototypeCreation”方法實(shí)現(xiàn)將原型注冊(cè)為當(dāng)前正在創(chuàng)建中,然后通過“createBean”方法創(chuàng)建并注冊(cè)一個(gè)新對(duì)象,通過“afterPrototypeCreation”方法執(zhí)行創(chuàng)建原型后的回調(diào),將原型標(biāo)記為不再處于創(chuàng)建狀態(tài),最后通過“getObjectForBeanInstance”方法獲取給定 bean 實(shí)例的對(duì)象。
如果既不是單例模式也不是原型模式,則根據(jù)Bean配置的生命周期,實(shí)例化Bean對(duì)象,如:request、session、application等生命周期
檢查所需類型是否與實(shí)際 bean 實(shí)例的類型匹配,由于requiredType為空,這里不做考慮
在這里我們重點(diǎn)看下“createBean”方法,在這里會(huì)創(chuàng)建一個(gè)bean實(shí)例。



步驟四:創(chuàng)建一個(gè) bean 實(shí)例
大致業(yè)務(wù)邏輯如下:
為給定的合并 bean 定義(和參數(shù))創(chuàng)建一個(gè) bean 實(shí)例。如果是子定義,bean 定義將已經(jīng)與父定義合并。
所有 bean 檢索方法都委托給此方法以進(jìn)行實(shí)際的 bean 創(chuàng)建。
此類的中心方法:創(chuàng)建 bean 實(shí)例、填充 bean 實(shí)例、應(yīng)用后處理器等。
在這個(gè)方法中我們看到一個(gè)以“doXXX”開頭的方法,他就是真正創(chuàng)建實(shí)例的方法。這個(gè)方法大致業(yè)務(wù)邏輯如下:
resolveBeanClass:判斷需要?jiǎng)?chuàng)建的bean是否可以實(shí)例化,即是否可以通過當(dāng)前的類加載器加載
prepareMethodOverrides:驗(yàn)證并準(zhǔn)備 bean 定義的方法覆蓋
resolveBeforeIInstantiation:如果該bean配置了實(shí)例化前后處理器,則BeanPostProcessors返回一個(gè)代理對(duì)象而不是bean實(shí)例,注意在這里spring實(shí)現(xiàn)了AOP
doCreateBean:實(shí)際創(chuàng)建指定的bean實(shí)例

步驟五:實(shí)際創(chuàng)建指定的bean
此時(shí)已經(jīng)進(jìn)行了預(yù)創(chuàng)建處理,例如檢查 postProcessBeforeInstantiation 回調(diào)。區(qū)分默認(rèn) bean 實(shí)例化、使用工廠方法和自動(dòng)裝配構(gòu)造函數(shù)。
大致業(yè)務(wù)邏輯如下:
createBeanInstance:把Bean對(duì)象封裝成BeanWrapper對(duì)象
applyMergedBeanDefiinitionPostProcessors:應(yīng)用后置處理器
addSingletonFactory:向容器中緩存單例對(duì)象來防止循環(huán)引用
populateBean:使用 bean 定義中的屬性值填充 BeanWrapper 中的 bean 實(shí)例
initializeBean:初始化實(shí)例
獲取已經(jīng)注冊(cè)單例模式的bean對(duì)象,如果根據(jù)名稱獲取的已注冊(cè)的bean和正在實(shí)例化的bean是同一個(gè),那么當(dāng)前實(shí)例化的bean初始化完成;否則,當(dāng)前bean依賴其他bean,并且當(dāng)發(fā)生循環(huán)引用時(shí)不允許新創(chuàng)建實(shí)例對(duì)象,獲取當(dāng)前bean所依賴的其他bean,對(duì)依賴bean進(jìn)行類型檢查
將 bean 添加到容器中


步驟六:把Bean對(duì)象封裝成BeanWrapper對(duì)象
使用適當(dāng)?shù)膶?shí)例化策略為指定的 bean 創(chuàng)建一個(gè)新實(shí)例:工廠方法、構(gòu)造函數(shù)自動(dòng)裝配或簡(jiǎn)單實(shí)例化。
大致業(yè)務(wù)邏輯如下:
resolveBeanClass:確保此時(shí)實(shí)際解析了 bean 類
obtainFromSupplier:從給定的supplier獲取一個(gè) bean 實(shí)例
instantiateUsingFactoryMethod:使用工廠方法獲取一個(gè) bean 實(shí)例
autowireConstructor:按照參數(shù)類型匹配bean的構(gòu)造方法獲取一個(gè)bean實(shí)例
determiineConstructorsFromBeanPostProcessors:確定首選的構(gòu)造函數(shù)
autowireConstructor:使用首選的構(gòu)造函數(shù)獲取一個(gè)bean實(shí)例
instantiateBean:使用無參構(gòu)造函數(shù)獲取一個(gè)bean實(shí)例

步驟七:使用默認(rèn)構(gòu)造函數(shù)實(shí)例化 bean
大致業(yè)務(wù)邏輯如下:
獲取系統(tǒng)的安全管理接口非空,使用匿名內(nèi)部類,根據(jù)實(shí)例化策略創(chuàng)建實(shí)例對(duì)象;否則使用默認(rèn)策略來創(chuàng)建實(shí)例,默認(rèn)策略類是SimpleInstantiationStrategy類
initBeanWrapper:使用在該工廠注冊(cè)的自定義編輯器初始化給定的 BeanWrapper

步驟八:使用初始化策略實(shí)例化bean對(duì)象
這個(gè)方法業(yè)務(wù)很簡(jiǎn)單:如果bean有方法被覆蓋了,則使用JDK的反射機(jī)制進(jìn)行實(shí)例化,否則,使用CGLib進(jìn)行實(shí)例化。

步驟九:使用JDK的反射機(jī)制實(shí)例化bean對(duì)象

步驟十:使用CGLib實(shí)例化bean對(duì)象
大致業(yè)務(wù)邏輯如下:
createEnhancedSubclass:使用 CGLIB 為提供的 bean 定義創(chuàng)建 bean 類的增強(qiáng)子類
Bean.instantiateClass:如果構(gòu)造器為空,使用JDK的反射機(jī)制實(shí)例化bean對(duì)象
enhancedSubclassConstructor:使用CGLib實(shí)例化bean對(duì)象


步驟十一:使用 bean 定義中的屬性值填充 BeanWrapper 中的 bean 實(shí)例
這個(gè)方法雖然代碼量很多,其實(shí)業(yè)務(wù)很簡(jiǎn)單:
獲取容器在解析Bean定義資源時(shí)為BeanDefinition中設(shè)置的屬性值PropertyValues
applyPropertyValues:對(duì)屬性進(jìn)行注入


步驟十二:解析并注入依賴屬性
應(yīng)用給定的屬性值,解析對(duì)此 bean 工廠中其他 bean 的任何運(yùn)行時(shí)引用。必須使用深拷貝,所以我們不會(huì)永久修改這個(gè)屬性。
分析下面代碼,我們可以看出,對(duì)屬性的注入過程分以下兩種情況:
屬性值類型不需要強(qiáng)制轉(zhuǎn)換時(shí),不需要解析屬性值,直接準(zhǔn)備進(jìn)行依賴注入
屬性值需要進(jìn)行類型強(qiáng)制轉(zhuǎn)換時(shí),如對(duì)其他對(duì)象的引用等,首先需要解析屬性值,然后對(duì)解析后的 屬性值進(jìn)行依賴注入
對(duì)屬性值的解析是在 BeanDefinitionValueResolver 類中的 resolveValueIfNecessary()方法中進(jìn)行的, 對(duì)屬性值的依賴注入是通過 bw.setPropertyValues()方法實(shí)現(xiàn)的。


步驟十三:解析屬性注入規(guī)則
在這個(gè)方法中我們看到對(duì)多種情況進(jìn)行解析,比如:對(duì)引用類型的屬性進(jìn)行解析(RuntimeBeanReference)、對(duì)屬性值是引用容器中另一個(gè)Bean名稱的解析(RuntimeBeanNameReference)、對(duì)Bean類型屬性的解析,主要是Bean中的內(nèi)部類(BeanDefinitionHolder)、對(duì)集合數(shù)組類型的屬性解析(ManagedArray)等等。

通過上面的代碼分析,我們明白了 Spring 是如何將引用類型,內(nèi)部類以及集合類型等屬性進(jìn)行解析的, 屬性值解析完成后就可以進(jìn)行依賴注入了,依賴注入的過程就是 Bean 對(duì)象實(shí)例設(shè)置到它所依賴的 Bean 對(duì)象屬性上去。
下篇文章將將講解真正的依賴注入(bw.setPropertyValues),該方法使用了委托模式。
時(shí)序圖

寫在最后
好兄弟可以點(diǎn)贊并關(guān)注我的公眾號(hào)“javaAnswer”,全部都是干貨。
