看看阿里p9對Spring ioc啟動原理的理解!

1. IOC概述

1.1 是什么?

兩個概念:控制反轉(zhuǎn),依賴注入

來看一下傳統(tǒng)的干活方式:在對象單一職責(zé)原則的基礎(chǔ)上,一個對象很少有不依賴其他對象而完成自己的工作,所以這個時候就會出現(xiàn)對象之間的依賴。而體現(xiàn)在我們的開發(fā)中,就是需要什么對象的時候,就創(chuàng)建什么對象,此時對象創(chuàng)建的控制權(quán)在我們自己手里。當對象創(chuàng)建的太多的時候,就會出現(xiàn)一個對象更改,就得更改所有依賴它的對象,耦合性大。自主性體現(xiàn)的同時也出現(xiàn)了對象耦合嚴重的情況。關(guān)于spring方面小編也整理了一套spring全家桶學(xué)習(xí)筆記,里面包含了全網(wǎng)最熱的spring問題總結(jié)。

這個時候,我們就會思考,能不能我們在用的時候直接拿到這個對象去用,而將創(chuàng)建對象的能力交給第三方,這樣我們就不需要關(guān)心對象是怎么創(chuàng)建的了。即將自己的控制權(quán)交出去。這就是控制反轉(zhuǎn)

這個時候,就會有另一個問題產(chǎn)生了,對象怎么才能直接被我們拿來用呢。對象創(chuàng)建的時候,我們把這個對象注入到這個對象中,然后就可以使用了。這就是依賴注入

另一個問題,耦合性怎么被解決掉的?通過控制反轉(zhuǎn)我們僅僅使用了這個對象,如果對象發(fā)生了修改,我們僅僅需要修改第三方創(chuàng)建對象的方式即可,這個時候難道還會出現(xiàn)所謂的對象耦合嗎?

2. IOC架構(gòu)

一個圖搞定,這個就是IOC的架構(gòu)思路,這不是其執(zhí)行流程圖。

我們接下來一步一步來解讀。

2.1 白話版

在第一章中我們了解了IOC是來幫助我們管理和創(chuàng)建對象的。

這個時候我們需要一個承載我們需要創(chuàng)建信息的容器,即圖中的XML或者注解,那么有了我們自己的BeanDefiniton信息以后,我們需要一個接口用來讀取這些信息,于是出現(xiàn)了BeanDefinitionReader用來讀取我們自己的Bean信息。

那么我們需要考慮一個問題了,那么多的對象怎么生產(chǎn)呢?

答案就是工廠模式。Spring默認的工廠是DefaultListableBeanFactory,沒錯,Spring中的所有對象(容器對象和我們自己創(chuàng)建的對象)都是由他創(chuàng)建的。大批量生產(chǎn)對象

這個時候又有了一個問題,我們不想通過BeanFactory直接生產(chǎn)了,需要對這個工廠進行一些特定處理,于是出現(xiàn)了BeanFactoryPostProcessor,用來對工廠做一些特定的處理。我們自己可以通過實現(xiàn)這個接口,進行自定義BeanFactory。又有兄弟說了:我想單獨創(chuàng)建一些我喜歡的對象,安排,F(xiàn)actoryBean誕生了,它可以幫助我們創(chuàng)建一個我們需要的對象(第四部分詳細解釋他們之間的區(qū)別)。

那又有兄弟說了:我想讓統(tǒng)一的對象創(chuàng)建之前按照我的方式進行一些特殊的行為,簡單,安排:see_no_evil

BeanPostProcessor出現(xiàn)了,他提供了兩個方法:一個在對象實例化之后初始化之前,執(zhí)行內(nèi)部的Before方法,在初始化之后,執(zhí)行After方法。(Bean生命周期,第四部分詳解

這個時候有兄弟有疑問了,不是說BeanPostProcessor在創(chuàng)建對象之前執(zhí)行嗎?怎么是創(chuàng)建完畢以后才執(zhí)行的Before方法。

如果各位兄弟了解過指令重排序這個概念,那么一定會聽過一個案例,創(chuàng)建一個對象需要三步

創(chuàng)建空間(實例化)

初始化

賦值

其中在初始化和賦值會出現(xiàn)指令重排序

根據(jù)這個點,應(yīng)該可以get到一個點,實例化和初始化不一樣。

所以又引出了一個點,我們對Bean進行一些操作,怎么操作,肯定是修改屬性,或者添加一些屬性等等,需要等待其在堆中開辟空間即實例化完成以后執(zhí)行吧。

所以BeanPostProcessor的before方法在實例化之后執(zhí)行,初始化之前執(zhí)行。

經(jīng)歷過前面一大堆的操作以后,終于我們的對象進入我們兜里了(容器里)。

關(guān)于銷毀,一般情況下我們通過ApplicationContext拿不到其銷毀方法,只能通過其子類實現(xiàn)獲取,關(guān)于銷毀同樣的流程,先執(zhí)行一個銷毀之前的操作,然后再銷毀。

其中在初始化和賦值會出現(xiàn)指令重排序

根據(jù)這個點,應(yīng)該可以get到一個點,實例化和初始化不一樣。

所以又引出了一個點,我們對Bean進行一些操作,怎么操作,肯定是修改屬性,或者添加一些屬性等等,需要等待其在堆中開辟空間即實例化完成以后執(zhí)行吧。

所以BeanPostProcessor的before方法在實例化之后執(zhí)行,初始化之前執(zhí)行。

經(jīng)歷過前面一大堆的操作以后,終于我們的對象進入我們兜里了(容器里)。

關(guān)于銷毀,一般情況下我們通過ApplicationContext拿不到其銷毀方法,只能通過其子類實現(xiàn)獲取,關(guān)于銷毀同樣的流程,先執(zhí)行一個銷毀之前的操作,然后再銷毀。

2.2 實際工作流程

看過Spring源碼或者聽過的都知道里面有一個方法叫做refresh,他完成了好多事情。當然他的行為也代表了整個IOC容器加載和實例化對象的過程。第三章的代碼解讀中我們仔細看

執(zhí)行過程:

加載配置文件,初始化系統(tǒng)環(huán)境Environment接口

準備上下文環(huán)境,初始化一些配置資源

創(chuàng)建一個工廠

為工廠添加各種環(huán)境

獲取子類自己重寫的BeanFactoryPostProcessor

執(zhí)行容器和我們自己的BeanFactoryPostProcessor

注冊BeanPostProcessor

國際化處理

轉(zhuǎn)播器

子類初始化Bean

注冊監(jiān)聽器,觀察者模式

完成Bean創(chuàng)建

發(fā)布相應(yīng)的事件,監(jiān)聽器

3. IOC源碼解讀

3.1 上下文配置啟動

在創(chuàng)建ClassPathXmlApplicationContext的時候,構(gòu)造方法中執(zhí)行了這些方法。

說白了,加載了一個解析配置文件路徑的加載器;然后又通過系統(tǒng)環(huán)境變量拿到這個配置文件,進行一些配置文件的去空格,轉(zhuǎn)換表達式等等操作(沒有進行解析);最后就是那個被我標成紅色東東,refresh方法中它完成了幾乎所有的工作。下面細聊

3.2 refresh

這個方法幾乎完成了所有的操作,創(chuàng)建工廠,執(zhí)行Processor等等,實例化對象,開啟事件監(jiān)聽等等。

接下來細聊

3.3.1 prepareRefresh()

這個方法的主要作用是為應(yīng)用上下文的刷新做一些準備性的工作。校驗資源文件,設(shè)置啟動時間和活躍狀態(tài)等。

3.3.2 obtainFreshBeanFactory()

可以get到,它主要就是創(chuàng)建了一個工廠BeanFactory,并且解析了配置文件,加載了Bean定義信息(面試的時候直接答這個點就夠了,如果想說的可以將下面的bean信息加載聊聊)沒錯,標紅的就是咱接下來細聊的點

這個就是加載配置文件的過程,注意:此時仍然沒有解析,解析在標紅的下面

這個就是讀取的過程,具體解析流程來自parse中,這個直接調(diào)用了Java中的解析XML的類庫,有興趣自行翻閱,最后返回了一個Document對象。

通過Document對象,讀取內(nèi)部的標簽,執(zhí)行不同的方法,邏輯和MyBatis中解析配置文件的思想相同,大家自行翻閱。

此時所有的Bean定義信息都被保存到了BeanDefinitionRegistry接口,然后走子類DefaultListableBeanFactory工廠的注冊方法

3.3.3 prepareBeanFactory(beanFactory)

為BeanFactory準備一些環(huán)境,方便在實例化的時候使用,同時添加容器自己的BeanPostProcessor

3.3.4 postProcessBeanFactory

留給子類擴展的BeanFactoryPostProcessor,

3.3.5 invokeBeanFactoryPostProcessors(beanFactory)

這個類,涉及到了兩個接口。

BeanFactoryPostProcessor

BeanDefinitionRegistryPostProcessor接口,這個接口是BeanFactoryPostProcessor的子接口,它的優(yōu)先級比BeanFactoryPostProcessor更高

它的總體執(zhí)行流程是:先執(zhí)行BeanDefinitionRegistryPostProcessor的BeanFactoryPostProcessor,然后再執(zhí)行BeanFactoryPostProcessor

下圖是BeanDefinitionRegistryPostProcessor接口的處理過程

BeanFactoryPostProcessor的處理邏輯

總邏輯就是先分類,已經(jīng)處理過的直接跳過,沒有處理過的,分類處理,邏輯和上面的相同。

3.3.6 registerBeanPostProcessors

這個方法的邏輯和上面的一樣,只不過上面是直接執(zhí)行了BeanFactoryPostProcessor,而這個僅僅注冊沒執(zhí)行。

首先拿到工廠中所有的BeanPostProcessor類型的Bean,然后分類處理,排序注冊。

3.3.7 initMessageSource()

執(zhí)行國際化內(nèi)容

3.3.8 initApplicationEventMulticaster

創(chuàng)建了一個多播器,為添加Listener提供支持。

主要邏輯:

容器中是否存在applicationEventMulticaster,如果存在直接注冊

如果不存在,創(chuàng)建一個SimpleApplicationEventMulticaster,注冊到容器中。

3.3.9 onRefresh()

子類擴展

3.3.10 registerListeners()

觀察者模式的實現(xiàn)

3.3.11 finishBeanFactoryInitialization

這一部分的內(nèi)容太多了,所以采用代碼和圖解的方式來講解。

下圖是創(chuàng)建Bean的主要流程

按照圖中的序號一個一個說:

BeanDefinition是否需要合并。BeanDefinition根據(jù)不同類型的配置文件信息,會將Bean封裝到不同的Bean信息定義類中。比如我們常用的配置文件版的GenericBeanDefinition;注解掃描版的ScannedGenericBeanDefinition等等。

而在這個過程中就出現(xiàn)了,父定義和子定義,我們需要在實際處理定義信息的時候進行合并處理,主要有以下三個方面

存在父定義信息,使用父定義信息創(chuàng)建一個RootBeanDefinition,然后將自定義信息作為參數(shù)傳入。

不存在父定義信息,并且當前BeanDefinition是RootBeanDefintion類型的,直接返回一份RootBeanDefintion的克隆

不存在父定義信息,并且當前BeanDefintion不是RootBeanDefintiton類型的,直接通過該BeanDefintion構(gòu)建一個RootBeanDefintion返回

上面的流程也是源碼中的執(zhí)行流程

isFactoryBean。判斷是否為FactoryBean

**簡單介紹一下:**FactoryBean是讓開發(fā)者創(chuàng)建自己需要Bean接口。內(nèi)部提供了三個方法

當我們通過GetBean直接該Bean的時候,獲取到的是該工廠指定返回的Bean類型。如果想要獲取該Bean本身,需要通過一個前綴獲得&

再來看一個點,這個就是從容器中獲取Bean的主要方法,也是解決循環(huán)依賴的邏輯

來聊一下它是怎么解決循環(huán)引用的?

它引入了一個三級緩存的概念

在發(fā)生循環(huán)引用的時候,它首先通過ObejctFactory工廠將Bean創(chuàng)建出來,**此時的對象并沒有進行屬性賦值,僅僅在堆中開辟了空間。**然后將此時的Bean添加到earlySingletonObjects容器里,**也就是說這個容器中保存的Bean都是半成品。**而在之后的屬性賦值中,由于對象為單例的,所以其引用地址不會發(fā)生變化,即對象最終是完整的。

1.getBean。通過這個方法直接創(chuàng)建了所有的對象,這也是Spring最核心的方法了,先來看一下它整體的一個流程

它的主要邏輯是:

先拿到當前要實例化的Bean的真實名字,主要是為了處理FactoryBean,拿到以后,從當前容器中看是否已經(jīng)創(chuàng)建過該Bean,如果存在直接返回。

如果不存在,獲取其父工廠,如果父工廠不為空,而且當前容器中不存在當前Bean的信息,則嘗試從父工廠中獲取Bean定義信息,進行Bean實例化

如果父工廠為空,將當前Bean信息存放到alreadyCreated緩存中。

獲取當前Bean的合并信息(getMergedLocalBeanDefinition),查看當前Bean是否存在依賴,如果存在則判斷當前Bean和依賴 Bean 是否為循環(huán)依賴,如果不是循環(huán)依賴則先創(chuàng)建依賴Bean

判斷當前Bean的作用域。

如果當前Bean是單例對象,直接創(chuàng)建Bean實例

如果當前Bean是多例對象,將當前Bean信息添加到正在創(chuàng)建多例緩存中,創(chuàng)建完畢以后移除

如果當前Bean是其他類型,如Requtst,Session等類型,則自定義一個ObejctFacotry工廠,重寫getObject方法,創(chuàng)建對象

對象創(chuàng)建以后,判斷當前對象是否為自己需要的對象,如果是直接返回;如果不是進行類型轉(zhuǎn)換,如果類型轉(zhuǎn)換失敗,直接拋異常

接下來看一眼CreateBean的執(zhí)行

這個方法主要完成的事情是:通過Bean的名字拿到對應(yīng)的Class對象;如果當前Bean獲取到的Class對象不為空且該RootDefintiton可以直接獲取到該Bean,克隆一份Bean定義信息,方便之后使用。

驗證當前Bean上的@Override信息。執(zhí)行BeanPostProcessor,返回一個代理對象(如果存在代理的話), 如果不存在代理,則直接創(chuàng)建Bean

接下來我們來聊一下這個玩意——resolveBeforeInstantiation

來吧,繼續(xù),看一下那個前置處理器邏輯

后置處理器就不看了,就調(diào)用了所有的后置處理器,然后執(zhí)行了一遍,沒有其他邏輯。

接下來繼續(xù)我們的正題:doCreateBean

其大致流程如上圖:

先判斷以后是否單例,然后從FactoryBean緩存中看一下是否存在正在創(chuàng)建的Bean,如果存在拿出,如果不存在則創(chuàng)建一個當前Bean的包裝類實例。然后拿到這個類的實例和實例類型,執(zhí)行以后后置處理器。

當前Bean是否為單例,是否允許循環(huán)依賴,時候正在進行創(chuàng)建,如果是,創(chuàng)建一個當前Bean的ObejctFactory以解決循環(huán)依賴的問題

填充Bean的屬性,進行Bean的實例化。

查看早期容器緩存中(緩存中的二級緩存中是否有該Bean)。如果有,則說明存在循環(huán)依賴,則進行處理

先看循環(huán)依賴吧

接著來,createBeanInstance

Spring提供了三種方式創(chuàng)建對象的包裝:

通過供給者對象對象直接創(chuàng)建。obtainFromSupplier

通過工廠方法直接創(chuàng)建。

默認創(chuàng)建。構(gòu)造方法是否需要自動注入構(gòu)造方法不需要自動注入,調(diào)用默認的構(gòu)造方法

這個方法執(zhí)行完畢以后,你應(yīng)該知曉的一個點是:此時對象實例已經(jīng)創(chuàng)建了,剩下的就是執(zhí)行一系列增強器和初始化方法,屬性填充等等。

我們按照代碼執(zhí)行順序來,屬性填充即populateBean

這個方法執(zhí)行邏輯:

首先判斷傳入的Bean是否為null,如果為null則判斷Bean定義信息中是否存在屬性值,如果存在,異常;如果不存在跳過

當前Bean定義信息是否為合并以后的,如果是且此時的工廠中存在InstantiationAwareBeanPostProcessors,那么在屬性填充之前進行修改Bean的信息

拿到所有的屬性值,解析屬性值的自動注入方式,Type或者Name,進行自動注入

判斷是否存在InstantiationAwareBeanPostProcessors,修改之前設(shè)置的屬性

判斷是否存在依賴檢查,檢查依賴

屬性賦值

接下來看執(zhí)行初始化方法,就是調(diào)用BeanPostprocessor,init等方法

這個就是這個方法的執(zhí)行流程圖,相信到這個地方,大家應(yīng)該對于為什么BeanPostProcessor的before方法會在init方法執(zhí)行了解了。這個方法的作用僅僅是用來進行一個生命周期的打印,對象在之前已經(jīng)創(chuàng)建了。

接下來看一下銷毀的方法。registerDisposableBeanIfNecessary

對于單例Bean來說,Spring將需要銷毀的Bean存放到了disposableBeans緩存中,通過DisposableBeanAdapter封裝了銷毀Bean

對于其他作用域來說,自定義了銷毀回調(diào)函數(shù),不過最后還是封裝為DisposableBeanAdapter

在封裝為DisposableBeanAdapter的過程中,會首先判斷該Bean中是否存在destroy方法,然后給賦值給destroyMethodName變量。再次判斷這個方法的參數(shù),如果參數(shù)的個數(shù)大于1,則拋出異常

3.3.12 finishRefresh

這個方法進行了一系列的資源清理和

initLifecycleProcessor,這個方法極具簡單,就看一下當前Bean中是否存在生命周期處理器,如果存在直接使用這個,如果不存在則創(chuàng)建一個默認的,并且注冊為一個單例的扔到容器中,今日份讀者福利:轉(zhuǎn)發(fā)+關(guān)注 獲取小編整理好的微服務(wù)全家桶學(xué)習(xí)筆記!

最后

喜歡小編今日的分享,記得關(guān)注我點贊喲,感謝支持!重要的事情說三遍,轉(zhuǎn)發(fā)+轉(zhuǎn)發(fā)+轉(zhuǎn)發(fā),

作者:麒麟改bug

鏈接:https://juejin.cn/post/6935337468531048455

來源:掘金

著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容