天天看點

阿裡三面:說說你對Spring ioc啟動原理的了解!1. IOC概述2. IOC架構3. IOC源碼解讀最後

1.1 是什麼?

兩個概念:控制反轉,依賴注入

來看一下傳統的幹活方式:在對象單一職責原則的基礎上,一個對象很少有不依賴其他對象而完成自己的工作,是以這個時候就會出現對象之間的依賴。而展現在我們的開發中,就是需要什麼對象的時候,就建立什麼對象,此時對象建立的控制權在我們自己手裡。當對象建立的太多的時候,就會出現一個對象更改,就得更改所有依賴它的對象,耦合性大。自主性展現的同時也出現了對象耦合嚴重的情況,關于spring方面小編也整理了一套spring全家桶學習筆記,裡面包含了全網最熱的spring問題總結。

這個時候,我們就會思考,能不能我們在用的時候直接拿到這個對象去用,而将建立對象的能力交給第三方,這樣我們就不需要關心對象是怎麼建立的了。即将自己的控制權交出去。這就是控制反轉

這個時候,就會有另一個問題産生了,對象怎麼才能直接被我們拿來用呢。對象建立的時候,我們把這個對象注入到這個對象中,然後就可以使用了。這就是依賴注入

另一個問題,耦合性怎麼被解決掉的?通過控制反轉我們僅僅使用了這個對象,如果對象發生了修改,我們僅僅需要修改第三方建立對象的方式即可,這個時候難道還會出現所謂的對象耦合嗎?

阿裡三面:說說你對Spring ioc啟動原理的了解!1. IOC概述2. IOC架構3. IOC源碼解讀最後

一個圖搞定,這個就是IOC的架構思路,這不是其執行流程圖。

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

2.1 白話版

在第一章中我們了解了IOC是來幫助我們管理和建立對象的。

這個時候我們需要一個承載我們需要建立資訊的容器,即圖中的XML或者注解,那麼有了我們自己的BeanDefiniton資訊以後,我們需要一個接口用來讀取這些資訊,于是出現了BeanDefinitionReader用來讀取我們自己的Bean資訊。

那麼我們需要考慮一個問題了,那麼多的對象怎麼生産呢?

答案就是工廠模式。Spring預設的工廠是DefaultListableBeanFactory,沒錯,Spring中的所有對象(容器對象和我們自己建立的對象)都是由他建立的。大批量生産對象

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

那又有兄弟說了:我想讓統一的對象建立之前按照我的方式進行一些特殊的行為,簡單,安排:see_no_evil

BeanPostProcessor出現了,他提供了兩個方法:一個在對象執行個體化之後初始化之前,執行内部的Before方法,在初始化之後,執行After方法。(Bean生命周期,第四部分詳解)

這個時候有兄弟有疑問了,不是說BeanPostProcessor在建立對象之前執行嗎?怎麼是建立完畢以後才執行的Before方法。

如果各位兄弟了解過指令重排序這個概念,那麼一定會聽過一個案例,建立一個對象需要三步

建立空間(執行個體化)

初始化

指派

其中在初始化和指派會出現指令重排序

根據這個點,應該可以get到一個點,執行個體化和初始化不一樣。

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

是以BeanPostProcessor的before方法在執行個體化之後執行,初始化之前執行。

經曆過前面一大堆的操作以後,終于我們的對象進入我們兜裡了(容器裡)。

關于銷毀,一般情況下我們通過ApplicationContext拿不到其銷毀方法,隻能通過其子類實作擷取,關于銷毀同樣的流程,先執行一個銷毀之前的操作,然後再銷毀。

2.2 實際工作流程

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

阿裡三面:說說你對Spring ioc啟動原理的了解!1. IOC概述2. IOC架構3. IOC源碼解讀最後

執行過程:

加載配置檔案,初始化系統環境Environment接口

準備上下文環境,初始化一些配置資源

建立一個工廠

為工廠添加各種環境

擷取子類自己重寫的BeanFactoryPostProcessor

執行容器和我們自己的BeanFactoryPostProcessor

注冊BeanPostProcessor

國際化處理

轉播器

子類初始化Bean

注冊監聽器,觀察者模式

完成Bean建立

釋出相應的事件,監聽器

3.1 上下文配置啟動

阿裡三面:說說你對Spring ioc啟動原理的了解!1. IOC概述2. IOC架構3. IOC源碼解讀最後

在建立ClassPathXmlApplicationContext的時候,構造方法中執行了這些方法。

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

3.2 refresh

阿裡三面:說說你對Spring ioc啟動原理的了解!1. IOC概述2. IOC架構3. IOC源碼解讀最後

這個方法幾乎完成了所有的操作,建立工廠,執行Processor等等,執行個體化對象,開啟事件監聽等等。

接下來細聊

3.3.1 prepareRefresh()

這個方法的主要作用是為應用上下文的重新整理做一些準備性的工作。校驗資源檔案,設定啟動時間和活躍狀态等。

3.3.2 obtainFreshBeanFactory()

阿裡三面:說說你對Spring ioc啟動原理的了解!1. IOC概述2. IOC架構3. IOC源碼解讀最後

可以get到,它主要就是建立了一個工廠BeanFactory,并且解析了配置檔案,加載了Bean定義資訊(面試的時候直接答這個點就夠了,如果想說的可以将下面的bean資訊加載聊聊)沒錯,标紅的就是咱接下來細聊的點

阿裡三面:說說你對Spring ioc啟動原理的了解!1. IOC概述2. IOC架構3. IOC源碼解讀最後

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

阿裡三面:說說你對Spring ioc啟動原理的了解!1. IOC概述2. IOC架構3. IOC源碼解讀最後

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

通過Document對象,讀取内部的标簽,執行不同的方法,邏輯和MyBatis中解析配置檔案的思想相同,大家自行翻閱。

此時所有的Bean定義資訊都被儲存到了BeanDefinitionRegistry接口,然後走子類DefaultListableBeanFactory工廠的注冊方法

阿裡三面:說說你對Spring ioc啟動原理的了解!1. IOC概述2. IOC架構3. IOC源碼解讀最後

3.3.3 prepareBeanFactory(beanFactory)

為BeanFactory準備一些環境,友善在執行個體化的時候使用,同時添加容器自己的BeanPostProcessor

阿裡三面:說說你對Spring ioc啟動原理的了解!1. IOC概述2. IOC架構3. IOC源碼解讀最後

3.3.4 postProcessBeanFactory

留給子類擴充的BeanFactoryPostProcessor,

3.3.5 invokeBeanFactoryPostProcessors(beanFactory)

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

BeanFactoryPostProcessor

BeanDefinitionRegistryPostProcessor接口,這個接口是BeanFactoryPostProcessor的子接口,它的優先級比BeanFactoryPostProcessor更高

它的總體執行流程是:先執行BeanDefinitionRegistryPostProcessor的BeanFactoryPostProcessor,然後再執行BeanFactoryPostProcessor

下圖是BeanDefinitionRegistryPostProcessor接口的處理過程

阿裡三面:說說你對Spring ioc啟動原理的了解!1. IOC概述2. IOC架構3. IOC源碼解讀最後

BeanFactoryPostProcessor的處理邏輯

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

3.3.6 registerBeanPostProcessors

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

阿裡三面:說說你對Spring ioc啟動原理的了解!1. IOC概述2. IOC架構3. IOC源碼解讀最後

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

3.3.7 initMessageSource()

執行國際化内容

3.3.8 initApplicationEventMulticaster

建立了一個多點傳播器,為添加Listener提供支援。

主要邏輯:

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

如果不存在,建立一個SimpleApplicationEventMulticaster,注冊到容器中。

3.3.9 onRefresh()

子類擴充

3.3.10 registerListeners()

觀察者模式的實作

阿裡三面:說說你對Spring ioc啟動原理的了解!1. IOC概述2. IOC架構3. IOC源碼解讀最後

3.3.11 finishBeanFactoryInitialization

這一部分的内容太多了,是以采用代碼和圖解的方式來講解。
阿裡三面:說說你對Spring ioc啟動原理的了解!1. IOC概述2. IOC架構3. IOC源碼解讀最後

下圖是建立Bean的主要流程

阿裡三面:說說你對Spring ioc啟動原理的了解!1. IOC概述2. IOC架構3. IOC源碼解讀最後

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

BeanDefinition是否需要合并。BeanDefinition根據不同類型的配置檔案資訊,會将Bean封裝到不同的Bean資訊定義類中。比如我們常用的配置檔案版的GenericBeanDefinition;注解掃描版的ScannedGenericBeanDefinition等等。

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

存在父定義資訊,使用父定義資訊建立一個RootBeanDefinition,然後将自定義資訊作為參數傳入。

不存在父定義資訊,并且目前BeanDefinition是RootBeanDefintion類型的,直接傳回一份RootBeanDefintion的克隆

不存在父定義資訊,并且目前BeanDefintion不是RootBeanDefintiton類型的,直接通過該BeanDefintion建構一個RootBeanDefintion傳回

上面的流程也是源碼中的執行流程

阿裡三面:說說你對Spring ioc啟動原理的了解!1. IOC概述2. IOC架構3. IOC源碼解讀最後

isFactoryBean。判斷是否為FactoryBean

簡單介紹一下:FactoryBean是讓開發者建立自己需要Bean接口。内部提供了三個方法

阿裡三面:說說你對Spring ioc啟動原理的了解!1. IOC概述2. IOC架構3. IOC源碼解讀最後

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

阿裡三面:說說你對Spring ioc啟動原理的了解!1. IOC概述2. IOC架構3. IOC源碼解讀最後

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

阿裡三面:說說你對Spring ioc啟動原理的了解!1. IOC概述2. IOC架構3. IOC源碼解讀最後

來聊一下它是怎麼解決循環引用的?

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

阿裡三面:說說你對Spring ioc啟動原理的了解!1. IOC概述2. IOC架構3. IOC源碼解讀最後

在發生循環引用的時候,它首先通過ObejctFactory工廠将Bean建立出來,此時的對象并沒有進行屬性指派,僅僅在堆中開辟了空間。然後将此時的Bean添加到earlySingletonObjects容器裡,也就是說這個容器中儲存的Bean都是半成品。而在之後的屬性指派中,由于對象為單例的,是以其引用位址不會發生變化,即對象最終是完整的。

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

阿裡三面:說說你對Spring ioc啟動原理的了解!1. IOC概述2. IOC架構3. IOC源碼解讀最後

它的主要邏輯是:

先拿到目前要執行個體化的Bean的真實名字,主要是為了處理FactoryBean,拿到以後,從目前容器中看是否已經建立過該Bean,如果存在直接傳回。

如果不存在,擷取其父工廠,如果父工廠不為空,而且目前容器中不存在目前Bean的資訊,則嘗試從父工廠中擷取Bean定義資訊,進行Bean執行個體化

如果父工廠為空,将目前Bean資訊存放到alreadyCreated緩存中。

擷取目前Bean的合并資訊(getMergedLocalBeanDefinition),檢視目前Bean是否存在依賴,如果存在則判斷目前Bean和依賴 Bean 是否為循環依賴,如果不是循環依賴則先建立依賴Bean

判斷目前Bean的作用域。

如果目前Bean是單例對象,直接建立Bean執行個體

如果目前Bean是多例對象,将目前Bean資訊添加到正在建立多例緩存中,建立完畢以後移除

如果目前Bean是其他類型,如Requtst,Session等類型,則自定義一個ObejctFacotry工廠,重寫getObject方法,建立對象

對象建立以後,判斷目前對象是否為自己需要的對象,如果是直接傳回;如果不是進行類型轉換,如果類型轉換失敗,直接抛異常

接下來看一眼CreateBean的執行

阿裡三面:說說你對Spring ioc啟動原理的了解!1. IOC概述2. IOC架構3. IOC源碼解讀最後

這個方法主要完成的事情是:通過Bean的名字拿到對應的Class對象;如果目前Bean擷取到的Class對象不為空且該RootDefintiton可以直接擷取到該Bean,克隆一份Bean定義資訊,友善之後使用。

驗證目前Bean上的@Override資訊。執行BeanPostProcessor,傳回一個代理對象(如果存在代理的話), 如果不存在代理,則直接建立Bean

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

阿裡三面:說說你對Spring ioc啟動原理的了解!1. IOC概述2. IOC架構3. IOC源碼解讀最後

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

阿裡三面:說說你對Spring ioc啟動原理的了解!1. IOC概述2. IOC架構3. IOC源碼解讀最後

後置處理器就不看了,就調用了所有的後置處理器,然後執行了一遍,沒有其他邏輯。

接下來繼續我們的正題:doCreateBean

阿裡三面:說說你對Spring ioc啟動原理的了解!1. IOC概述2. IOC架構3. IOC源碼解讀最後

其大緻流程如上圖:

先判斷以後是否單例,然後從FactoryBean緩存中看一下是否存在正在建立的Bean,如果存在拿出,如果不存在則建立一個目前Bean的包裝類執行個體。然後拿到這個類的執行個體和執行個體類型,執行以後後置處理器。

目前Bean是否為單例,是否允許循環依賴,時候正在進行建立,如果是,建立一個目前Bean的ObejctFactory以解決循環依賴的問題

填充Bean的屬性,進行Bean的執行個體化。

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

先看循環依賴吧

阿裡三面:說說你對Spring ioc啟動原理的了解!1. IOC概述2. IOC架構3. IOC源碼解讀最後

接着來,createBeanInstance

阿裡三面:說說你對Spring ioc啟動原理的了解!1. IOC概述2. IOC架構3. IOC源碼解讀最後

Spring提供了三種方式建立對象的包裝:

通過供給者對象對象直接建立。obtainFromSupplier

通過工廠方法直接建立。

預設建立。構造方法是否需要自動注入構造方法不需要自動注入,調用預設的構造方法

這個方法執行完畢以後,你應該知曉的一個點是:此時對象執行個體已經建立了,剩下的就是執行一系列增強器和初始化方法,屬性填充等等。

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

這個方法執行邏輯:

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

目前Bean定義資訊是否為合并以後的,如果是且此時的工廠中存在InstantiationAwareBeanPostProcessors,那麼在屬性填充之前進行修改Bean的資訊

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

判斷是否存在InstantiationAwareBeanPostProcessors,修改之前設定的屬性

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

屬性指派

阿裡三面:說說你對Spring ioc啟動原理的了解!1. IOC概述2. IOC架構3. IOC源碼解讀最後

接下來看執行初始化方法,就是調用BeanPostprocessor,init等方法

阿裡三面:說說你對Spring ioc啟動原理的了解!1. IOC概述2. IOC架構3. IOC源碼解讀最後

這個就是這個方法的執行流程圖,相信到這個地方,大家應該對于為什麼BeanPostProcessor的before方法會在init方法執行了解了。這個方法的作用僅僅是用來進行一個生命周期的列印,對象在之前已經建立了。

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

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

對于其他作用域來說,自定義了銷毀回調函數,不過最後還是封裝為DisposableBeanAdapter

在封裝為DisposableBeanAdapter的過程中,會首先判斷該Bean中是否存在destroy方法,然後給指派給destroyMethodName變量。再次判斷這個方法的參數,如果參數的個數大于1,則抛出異常

阿裡三面:說說你對Spring ioc啟動原理的了解!1. IOC概述2. IOC架構3. IOC源碼解讀最後

3.3.12 finishRefresh

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

阿裡三面:說說你對Spring ioc啟動原理的了解!1. IOC概述2. IOC架構3. IOC源碼解讀最後

initLifecycleProcessor,這個方法極具簡單,就看一下目前Bean中是否存在生命周期處理器,如果存在直接使用這個,如果不存在則建立一個預設的,并且注冊為一個單例的扔到容器中,今日份讀者福利:轉發+關注 擷取小編整理好的微服務全家桶學習筆記!

喜歡小編今日的分享,記得關注我點贊喲,感謝支援!重要的事情說三遍,轉發+轉發+轉發,一定要記得轉發 關注哦!!!