2020-09-25

Spring第二篇 Spring IOC

目錄

一登夫、概念梳理

1.什么是IoC和DI

二抓于、Bean的創(chuàng)建

1.IoC容器的初始化

2.Bean的注入方式

3.Bean解析注冊(cè)過(guò)程

4.Bean的創(chuàng)建過(guò)程

三尊剔、Bean的生命周期

1.Bean生命周期

2.Bean實(shí)例化順序

四、避坑指南

五梨水、參考文獻(xiàn)

作為一個(gè)后端開(kāi)發(fā)妄荔,我們的日常離不開(kāi)Spring,尤其是Spring的IoC泳挥,但是你真的了解Spring IoC其中的細(xì)節(jié)嗎?

Spring的Bean是怎么創(chuàng)建的呢至朗?bean的生命周期是怎么樣的屉符?其中有什么容易踩坑的點(diǎn)嗎?讓我們帶著疑問(wèn)一起來(lái)看看锹引。

一矗钟、概念梳理

1.什么是IoC和DI

IoC:控制反轉(zhuǎn)(Inversion of Control)容器,這不是什么技術(shù)嫌变,而是一種設(shè)計(jì)思想吨艇。在Java開(kāi)發(fā)中,Ioc意味著將你設(shè)計(jì)好的對(duì)象交給容器控制初澎,而不是傳統(tǒng)的在你的對(duì)象內(nèi)部直接控制秸应。

IoC一些解釋:

●誰(shuí)控制誰(shuí),控制什么:傳統(tǒng)Java SE程序設(shè)計(jì)碑宴,我們直接在對(duì)象內(nèi)部通過(guò)new進(jìn)行創(chuàng)建對(duì)象软啼,是程序主動(dòng)去創(chuàng)建依賴對(duì)象;而IoC是有專門一個(gè)容器來(lái)創(chuàng)建這些對(duì)象延柠,即由Ioc容器來(lái)控制對(duì)象的創(chuàng)建祸挪;誰(shuí)控制誰(shuí)?當(dāng)然是IoC 容器控制了對(duì)象贞间;控制什么贿条?那就是主要控制了外部資源獲取(不只是對(duì)象包括比如文件等)增热。

●為何是反轉(zhuǎn)整以,哪些方面反轉(zhuǎn)了:有反轉(zhuǎn)就有正轉(zhuǎn),傳統(tǒng)應(yīng)用程序是由我們自己在對(duì)象中主動(dòng)控制去直接獲取依賴對(duì)象峻仇,也就是正轉(zhuǎn)公黑;而反轉(zhuǎn)則是由容器來(lái)幫忙創(chuàng)建及注入依賴對(duì)象;為何是反轉(zhuǎn)摄咆?因?yàn)橛扇萜鲙臀覀儾檎壹白⑷胍蕾噷?duì)象凡蚜,對(duì)象只是被動(dòng)的接受依賴對(duì)象,所以是反轉(zhuǎn)吭从;哪些方面反轉(zhuǎn)了朝蜘?依賴對(duì)象的獲取被反轉(zhuǎn)了。

DI:依賴注入(Dependency Injection)涩金,組件之間依賴關(guān)系由容器在運(yùn)行期決定谱醇,形象的說(shuō)暇仲,即由容器動(dòng)態(tài)的將某個(gè)依賴關(guān)系注入到組件之中。依賴注入的目的并非為軟件系統(tǒng)帶來(lái)更多功能枣抱,而是為了提升組件重用的頻率熔吗,并為系統(tǒng)搭建一個(gè)靈活、可擴(kuò)展的平臺(tái)佳晶。IoC和DI其實(shí)是同一個(gè)概念的不同角度描述桅狠。

傳統(tǒng)獲取對(duì)象的方式與IoC方式對(duì)比:

二、Bean的創(chuàng)建

1.IoC容器的初始化

Spring IOC容器初始化的核心過(guò)程可以簡(jiǎn)單的如下圖表示轿秧,主要有四個(gè)步驟(還有一些如:后置加載器中跌,國(guó)際化,事件廣播器等一些過(guò)程不展開(kāi)):

Bean定義的定位菇篡,Bean 可能定義在XML中漩符,或者一個(gè)注解,或者其他形式驱还。這些都被用Resource來(lái)定位嗜暴,讀取Resource獲取BeanDefinition 注冊(cè)到 Bean定義注冊(cè)表中。

第一次向容器getBean操作會(huì)觸發(fā)Bean的創(chuàng)建過(guò)程,實(shí)列化一個(gè)Bean時(shí) ,根據(jù)BeanDefinition中類信息等實(shí)列化Bean议蟆。

將實(shí)列化的Bean放到單列Bean緩存內(nèi)闷沥。

此后再次獲取向容器getBean就會(huì)從緩存中獲取。


擴(kuò)展閱讀:IoC容器初始化源碼解析Spring IOC容器初始化

2.Bean的注入方式

a.常用的注入方式

bean的注入方式是老生常談的話題了咐容,這里不展開(kāi)細(xì)說(shuō)舆逃。

目前主要有五種注入方式:SET注入,構(gòu)造器注入戳粒,靜態(tài)工廠路狮,實(shí)例工廠,注解方式

參考:Spring Bean注入

b.常用的Bean的配置參數(shù)

屬性含義說(shuō)明

idbean的唯一標(biāo)識(shí)

class類的完全限定名

parent父類bean定義的名字如果沒(méi)有任何聲明蔚约,會(huì)使用父bean奄妨,但是也可以重寫父類。重寫父類時(shí)苹祟,子bean 必須與父bean 兼容砸抛,也就是說(shuō),接受父類的屬性值和構(gòu)造器聲明的值苔咪。

子bean會(huì)繼承父bean的構(gòu)造器聲明的值锰悼,屬性值柳骄,并且重寫父bean的方法团赏。如果init方法,destroy方法已聲明耐薯,他們會(huì)覆蓋父bean相應(yīng)的設(shè)置舔清。保留的設(shè)置會(huì)直接從子bean獲取丝里,包括depends on,auto wire mode体谒,scope杯聚,lazy init.

abstract聲明bean是否是抽象的該屬性決定該類是否會(huì)實(shí)例化。默認(rèn)是false抒痒。

注意:abstract屬性不會(huì)被子bean繼承幌绍,所以,abstract為true時(shí)需要對(duì)每個(gè)bean顯示聲明故响。

lazy-init決定是否延遲實(shí)例化如果為false,則啟動(dòng)時(shí)會(huì)立即實(shí)例化單例模式的bean傀广。默認(rèn)是false。

注意:lazy-init屬性不會(huì)被子bean繼承彩届。

autowire決定是否自動(dòng)裝配bean的屬性autowire有4中模式(該屬性不會(huì)被子bean繼承伪冰。):

1."no":Spring默認(rèn)的模式。bean的引用必須在XML文件中通過(guò)<ref/>元素或ref屬性顯示定義樟蠕。

2."byName":通過(guò)屬性名使用自動(dòng)裝配贮聂。如果一個(gè)Cat類擁有一個(gè)dog屬性,那么Spring會(huì)根據(jù)名字dog去尋找bean,如果沒(méi)有找到bean,則不會(huì)自動(dòng)裝配寨辩。

3."byType":如果Spring容器只有該屬性類型的一個(gè)bean吓懈,會(huì)自動(dòng)裝配。當(dāng)有多個(gè)該屬性類型的bean時(shí)會(huì)報(bào)錯(cuò)捣染。如果沒(méi)有骄瓣,則不會(huì)自動(dòng)裝配。

4."constructor":針對(duì)構(gòu)造器引用耍攘,和byType類似榕栏。

參考:Spring中的自動(dòng)裝配

depends-on該bean初始化時(shí)依賴的其他beanbean工廠確保其他bean在該bean之前完成初始化。

注意:依賴項(xiàng)一般通過(guò)bean屬性或構(gòu)造器聲明蕾各,這個(gè)屬性對(duì)其他依賴(如靜態(tài)類或啟動(dòng)階段數(shù)據(jù)庫(kù)的準(zhǔn)備)是必要的扒磁。

注意:depends-on屬性不會(huì)被子bean繼承。

scopebean的作用域singleton:單例模式式曲,默認(rèn)選項(xiàng)

prototype:非單例模式

request:對(duì)于web應(yīng)用妨托,每一個(gè)請(qǐng)求產(chǎn)生一個(gè)新的實(shí)例

session:對(duì)于web應(yīng)用,一個(gè)session產(chǎn)生一個(gè)實(shí)例

參考:Spring Bean的scope

init-method初始化方法bean創(chuàng)建時(shí)的初始化方法

destroy-method銷毀方法bean銷毀時(shí)調(diào)用的方法吝羞,僅僅在singleton模式下起作用

擴(kuò)展閱讀:自定義命名空間:2. xml自定義命名空間解析

c.常用的注解:Spring常用注解

3.Bean解析注冊(cè)過(guò)程

這個(gè)過(guò)程完成Bean的從配置文件到解析注冊(cè)成bean工廠的過(guò)程(對(duì)應(yīng)代碼在AbstactApplicationContext.obtainFreshBeanFactory)



XML

Resource

BeanDefinition

BeanFactory

讀取

解析

注冊(cè)

通過(guò)讀取XML配置文件獲取 Resource 資源兰伤,獲取這個(gè)資源包含了路徑config/spring/local/appcontext-client.xml 文件下定義的BeanDefinition信息。

創(chuàng)建一個(gè) BeanFactory钧排,這里使用 DefaultListableBeanFactory敦腔。

創(chuàng)建一個(gè)載入 BeanDefinition 的解讀器,這里使用 XmlBeanDefinitionReader 來(lái)載入 XML 文件形式 BeanDefinition恨溜,通過(guò)一個(gè)回調(diào)配置給 factory符衔。

從定義好的資源位置讀入配置信息找前,具體的解析過(guò)程由 XmlBeanDefinitionReader 的 loadBeanDefinitions() 方法來(lái)完成。完成整個(gè)載入和注冊(cè) Bean 定義之后判族,需要的 IoC 容器就建立起來(lái)可以直接使用了躺盛。

4.Bean的創(chuàng)建過(guò)程

這個(gè)過(guò)程完成Bean的實(shí)例化,如果是singleton類型的Bean還會(huì)放入緩存池中形帮。(對(duì)應(yīng)源碼:AbstactApplicationContext.finishBeanFactoryInitialization)

start

轉(zhuǎn)換對(duì)應(yīng)的beanName

嘗試從緩存中加載單例

N

Y

緩存中加載的單例為空

doGetBean

transformedBeanName

getSingleton

Bean實(shí)例化

getObjectForBeanInstance

類型轉(zhuǎn)換

end

原型模式依賴檢查

檢測(cè)是否到父類工廠加載bean

存在循環(huán)依賴且原型bean槽惫,拋異常中斷加載

isPrototypeCurrentlyInCreation

parentBeanFactory != null && !containsBeanDefinition(beanName)

轉(zhuǎn)化為RootBeanDefinition

getMergedLocalBeanDefinition

尋找Bean的依賴

getDependsOn

Y

N

存在依賴的bean

優(yōu)先加載依賴的bean

針對(duì)不同的scope創(chuàng)建bean

單例模式

原型模式

bean的模式

getBean

getSingleton

根據(jù)單例模式創(chuàng)建bean

創(chuàng)建bean

bean實(shí)例化

類型轉(zhuǎn)換

指定的scope上創(chuàng)建bean

創(chuàng)建bean

bean實(shí)例化

createBean

getObjectForBeanInstance

getObjectForBeanInstance

createBean

bean實(shí)例化

end

getObjectForBeanInstance

name參數(shù)可能是別名或者FactoryBean,需要一系列解析

單例的bean先從緩存中加載

加載失敗再嘗試從singletonFactory中加載

為避免循環(huán)依賴辩撑,先將bean的ObjectFactory加入緩存

獲取的bean如果

符合requireType指定類型則直接返回

否則轉(zhuǎn)化為指定類型

這里得到的是工廠bean的初始狀態(tài)躯枢,我們真正想要的是工廠bean中定義的factory-method方法中返回的bean

遞歸調(diào)用,

優(yōu)先加載當(dāng)前bean的依賴bean

擴(kuò)展閱讀:Spring創(chuàng)建Bean過(guò)程源碼解析:Spring-IOC源碼解讀Spring源碼深度解析

三槐臀、Bean的生命周期

1.Bean生命周期

Bean生命周期是IOC中非常核心的內(nèi)容锄蹂,Spring為我們預(yù)留了很多的接口,讓我們?cè)赽ean實(shí)例化前后水慨、初始化前后可以寫入一些自己的邏輯得糜。

實(shí)例化Bean對(duì)象

設(shè)置對(duì)象屬性

檢查Aware相關(guān)接口并設(shè)置相關(guān)依賴

BeanPostProcessor前置處理

檢查是否是InitializingBean以決定是否調(diào)用afterPropertiesSet方法

檢查是否配有自定義的init-method

BeanPostProcessor后置處理

注冊(cè)必要的Destruction相關(guān)回調(diào)接口

使用中

是否實(shí)現(xiàn)DisposableBean接口

是否配置有自定義的DisposableBean接口

*Bean的生命周期

1. 根據(jù)BeanDefinition信息,實(shí)例化對(duì)象晰洒,Constructor構(gòu)造方法;

2. 根據(jù)BeanDefinition信息朝抖,配置Bean的所有屬性(將bean的引用注入到bean對(duì)應(yīng)的屬性,*可能存在循環(huán)依賴問(wèn)題);

3. 如果Bean實(shí)現(xiàn)了BeanNameAware接口谍珊,工廠調(diào)用Bean的setBeanName治宣,參數(shù)為Bean的Id;

4. 如果Bean實(shí)現(xiàn)BeanFactoryAware接口,Spring將調(diào)用setBeanFactory()方法砌滞,將BeanFactory容器實(shí)例傳入侮邀;

5. 如果Bean實(shí)現(xiàn)ApplicationContextAware接口,Spring將調(diào)用setApplicationContext()方法贝润,將bean所在的應(yīng)用上下文的引用傳入進(jìn)來(lái)绊茧;

5. 如果存在類實(shí)現(xiàn)了BeanPostProcessor接口,執(zhí)行這些實(shí)現(xiàn)類的postProcessBeforeInitialization方法打掘,這相當(dāng)于在Bean初始化之前插入邏輯 华畏;

6. 如果Bean實(shí)現(xiàn)InitializingBean接口, 執(zhí)行afterPropertiesSet方法尊蚁;

7. 如果Bean指定了init-method方法亡笑,就會(huì)調(diào)用該方法。例:\<bean init-method="init"> 横朋;

8. 如果存在類實(shí)現(xiàn)了BeanPostProcessor接口仑乌,執(zhí)行這些實(shí)現(xiàn)類的postProcessAfterInitialization方法,這相當(dāng)于在Bean初始化之后插入邏輯 ;

9. 這個(gè)階段Bean已經(jīng)可以使用了绝骚,scope為singleton的Bean會(huì)被緩存在IOC容器中

10. 如果Bean實(shí)現(xiàn)了DisposableBean接口, 在容器銷毀的時(shí)候執(zhí)行destroy方法祠够。

11. 如果配置了destory-method方法压汪,就調(diào)用該方法。例:\<bean destroy-method="customerDestroy">

擴(kuò)展閱讀:Spring的擴(kuò)展接口 :Spring 擴(kuò)展接口

2.Bean實(shí)例化順序

轉(zhuǎn)存失敗重新上傳取消?

1.解析內(nèi)部類古瓤,查看內(nèi)部類是否應(yīng)該被定義成一個(gè)Bean止剖,如果是,遞歸解析落君。

2.解析@PropertySource穿香,也就是解析被引入的Properties文件。

3.解析配置類上是否有@ComponentScan注解绎速,如果有則執(zhí)行掃描動(dòng)作皮获,通過(guò)掃描得到的Bean Class會(huì)被立即解析成BeanDefinition,添加進(jìn)beanDefinitionNames屬性中纹冤。之后查看掃描到的Bean Class是否是一個(gè)配置類(大部分情況是洒宝,因?yàn)闃?biāo)識(shí)@Component注解),如果是則遞歸解析這個(gè)Bean Class萌京。

4.解析@Import引入的類雁歌,如果這個(gè)類是一個(gè)配置類,則遞歸解析知残。

5.解析@Bean標(biāo)識(shí)的方法靠瞎,此種形式定義的Bean Class不會(huì)被遞歸解析

6.解析父類上的@ComponentScan,@Import求妹,@Bean乏盐,父類不會(huì)被再次實(shí)例化,因?yàn)槠渥宇惸軌蜃龈割惖墓ぷ髦苹校恍枰~外的Bean了丑勤。

Spring 解決bean的循環(huán)依賴:05、spring ioc-bean的循環(huán)依賴

Spring 的bean加載順序:Spring Bean加載順序

四吧趣、避坑指南

相關(guān)case:

對(duì)bean生命周期了解不充分法竞,在static方法中使用getBean方法,此時(shí)bean還未初始化强挫。

2018-04-08 account-server上線大量NoClassDefFoundError錯(cuò)誤

2.Spring默認(rèn)同id名的bean會(huì)被覆蓋岔霸,一個(gè)解決辦法是不允許同id的bean覆蓋 參考:spring 同名bean問(wèn)題 分析和解決

CaseStudy-20170321-paynotice上線導(dǎo)致未發(fā)券

CaseStudy-20150113-支付成功后與指紋相關(guān)的數(shù)據(jù)沒(méi)有發(fā)送給aegis

2018年Q4-CaseStudy-20181110-促銷組-活動(dòng)創(chuàng)建引入新事務(wù)管理器導(dǎo)致促銷活動(dòng)數(shù)據(jù)不完整或未正常開(kāi)始

CaseStudy-20190108 [API研發(fā)組][bug] -indexapi讀取MCC失敗

門票客訴排序不生效case原因分析

3.Spring的bean加載順序。

Spring中Bean加載順序引起的坑

忽視Spring bean加載順序俯渤,造成多次執(zhí)行程序呆细,結(jié)果會(huì)有所不同

五、參考文獻(xiàn)

Spring官網(wǎng)IOC文檔

Spring-IOC源碼解讀

Spring源碼淺析及引申

spring ioc源碼學(xué)習(xí)

SpringBean生命周期詳解

https://blog.csdn.net/levena/article/details/52268472

http://www.reibang.com/p/f9d18f495635

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末八匠,一起剝皮案震驚了整個(gè)濱河市絮爷,隨后出現(xiàn)的幾起案子趴酣,更是在濱河造成了極大的恐慌,老刑警劉巖坑夯,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件岖寞,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡柜蜈,警方通過(guò)查閱死者的電腦和手機(jī)仗谆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)淑履,“玉大人隶垮,你說(shuō)我怎么就攤上這事∶卦耄” “怎么了狸吞?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)指煎。 經(jīng)常有香客問(wèn)我捷绒,道長(zhǎng),這世上最難降的妖魔是什么贯要? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任暖侨,我火速辦了婚禮,結(jié)果婚禮上崇渗,老公的妹妹穿的比我還像新娘字逗。我一直安慰自己,他們只是感情好宅广,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布葫掉。 她就那樣靜靜地躺著,像睡著了一般跟狱。 火紅的嫁衣襯著肌膚如雪俭厚。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,624評(píng)論 1 305
  • 那天驶臊,我揣著相機(jī)與錄音挪挤,去河邊找鬼。 笑死关翎,一個(gè)胖子當(dāng)著我的面吹牛扛门,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播纵寝,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼论寨,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起葬凳,我...
    開(kāi)封第一講書(shū)人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤绰垂,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后火焰,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體劲装,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年荐健,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片琳袄。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡江场,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出窖逗,到底是詐尸還是另有隱情址否,我是刑警寧澤,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布碎紊,位于F島的核電站佑附,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏仗考。R本人自食惡果不足惜音同,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望秃嗜。 院中可真熱鬧权均,春花似錦、人聲如沸锅锨。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)必搞。三九已至必指,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間恕洲,已是汗流浹背塔橡。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留霜第,地道東北人谱邪。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像庶诡,于是被迫代替她去往敵國(guó)和親惦银。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355