最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

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

2022-06-16 10:00 作者:做架構(gòu)師不做框架師  | 我要投稿


什么是依賴注入?

依賴注入(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”,全部都是干貨。


Spring DI三部曲之實(shí)例化的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
长泰县| 临朐县| 基隆市| 嘉黎县| 桐乡市| 瑞金市| 杨浦区| 咸丰县| 淄博市| 新和县| 双牌县| 西充县| 灌阳县| 沁水县| 绍兴县| 黄平县| 沛县| 水富县| 洛南县| 高陵县| 修水县| 无锡市| 唐河县| 苗栗市| 平罗县| 新巴尔虎右旗| 松桃| 武鸣县| 洛南县| 禹州市| 青龙| 洪泽县| 垣曲县| 偃师市| 砀山县| 罗田县| 遂川县| 高台县| 多伦县| 绿春县| 新郑市|