阿里三面:說說你對Spring ioc啓動原理的理解!

1. IOC概述

1.1 是什麼?面試

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

來看一下傳統的幹活方式:在對象單一職責原則的基礎上,一個對象不多有不依賴其餘對象而完成本身的工做,因此這個時候就會出現對象之間的依賴。而體如今咱們的開發中,就是須要什麼對象的時候,就建立什麼對象,此時對象建立的控制權在咱們本身手裏。當對象建立的太多的時候,就會出現一個對象更改,就得更改全部依賴它的對象,耦合性大。自主性體現的同時也出現了對象耦合嚴重的狀況架構

  • 這個時候,咱們就會思考,能不能咱們在用的時候直接拿到這個對象去用,而將建立對象的能力交給第三方,這樣咱們就不須要關心對象是怎麼建立的了。即將本身的控制權交出去。這就是控制反轉
  • 這個時候,就會有另外一個問題產生了,對象怎麼才能直接被咱們拿來用呢。對象建立的時候,咱們把這個對象注入到這個對象中,而後就可使用了。這就是依賴注入
  • 另外一個問題,耦合性怎麼被解決掉的?經過控制反轉咱們僅僅使用了這個對象,若是對象發生了修改,咱們僅僅須要修改第三方建立對象的方式便可,這個時候難道還會出現所謂的對象耦合嗎?

2. IOC架構

阿里三面:說說你對Spring ioc啓動原理的理解!

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

咱們接下來一步一步來解讀。ide

2.1 白話版函數

在第一章中咱們瞭解了IOC是來幫助咱們管理和建立對象的。微服務

這個時候咱們須要一個承載咱們須要建立信息的容器,即圖中的XML或者註解,那麼有了咱們本身的BeanDefiniton信息之後,咱們須要一個接口用來讀取這些信息,因而出現了BeanDefinitionReader用來讀取咱們本身的Bean信息。post

那麼咱們須要考慮一個問題了,那麼多的對象怎麼生產呢?學習

答案就是工廠模式。Spring默認的工廠是DefaultListableBeanFactory,沒錯,Spring中的全部對象(容器對象和咱們本身建立的對象)都是由他建立的。大批量生產對象3d

這個時候又有了一個問題,咱們不想經過BeanFactory直接生產了,須要對這個工廠進行一些特定處理,因而出現了BeanFactoryPostProcessor,用來對工廠作一些特定的處理。咱們本身能夠經過實現這個接口,進行自定義BeanFactory。又有兄弟說了:我想單首創建一些我喜歡的對象,安排,FactoryBean誕生了,它能夠幫助咱們建立一個咱們須要的對象(第四部分詳細解釋他們之間的區別)。

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

BeanPostProcessor出現了,他提供了兩個方法:一個在對象實例化以後初始化以前,執行內部的Before方法,在初始化以後,執行After方法。(Bean生命週期,第四部分詳解

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

若是各位兄弟瞭解過指令重排序這個概念,那麼必定會聽過一個案例,建立一個對象須要三步

  • 建立空間(實例化)
  • 初始化
  • 賦值

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

根據這個點,應該能夠get到一個點,實例化和初始化不同。

因此又引出了一個點,咱們對Bean進行一些操做,怎麼操做,確定是修改屬性,或者添加一些屬性等等,須要等待其在堆中開闢空間即實例化完成之後執行吧。

因此BeanPostProcessor的before方法在實例化以後執行,初始化以前執行。

經歷過前面一大堆的操做之後,終於咱們的對象進入咱們兜裏了(容器裏)。

關於銷燬,通常狀況下咱們經過ApplicationContext拿不到其銷燬方法,只能經過其子類實現獲取,關於銷燬一樣的流程,先執行一個銷燬以前的操做,而後再銷燬。

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

根據這個點,應該能夠get到一個點,實例化和初始化不同。

因此又引出了一個點,咱們對Bean進行一些操做,怎麼操做,確定是修改屬性,或者添加一些屬性等等,須要等待其在堆中開闢空間即實例化完成之後執行吧。

因此BeanPostProcessor的before方法在實例化以後執行,初始化以前執行。

經歷過前面一大堆的操做之後,終於咱們的對象進入咱們兜裏了(容器裏)。

關於銷燬,通常狀況下咱們經過ApplicationContext拿不到其銷燬方法,只能經過其子類實現獲取,關於銷燬一樣的流程,先執行一個銷燬以前的操做,而後再銷燬。

2.2 實際工做流程

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

阿里三面:說說你對Spring ioc啓動原理的理解!

執行過程:

  • 加載配置文件,初始化系統環境Environment接口
  • 準備上下文環境,初始化一些配置資源
  • 建立一個工廠
  • 爲工廠添加各類環境
  • 獲取子類本身重寫的BeanFactoryPostProcessor
  • 執行容器和咱們本身的BeanFactoryPostProcessor
  • 註冊BeanPostProcessor
  • 國際化處理
  • 轉播器
  • 子類初始化Bean
  • 註冊監聽器,觀察者模式
  • 完成Bean建立
  • 發佈相應的事件,監聽器

3. IOC源碼解讀

3.1 上下文配置啓動

阿里三面:說說你對Spring ioc啓動原理的理解!

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

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

3.2 refresh

阿里三面:說說你對Spring ioc啓動原理的理解!

這個方法幾乎完成了全部的操做,建立工廠,執行Processor等等,實例化對象,開啓事件監聽等等。

接下來細聊

3.3.1 prepareRefresh()

這個方法的主要做用是爲應用上下文的刷新作一些準備性的工做。校驗資源文件,設置啓動時間和活躍狀態等。

3.3.2 obtainFreshBeanFactory()

阿里三面:說說你對Spring ioc啓動原理的理解!

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

阿里三面:說說你對Spring ioc啓動原理的理解!

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

阿里三面:說說你對Spring ioc啓動原理的理解!

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

經過Document對象,讀取內部的標籤,執行不一樣的方法,邏輯和MyBatis中解析配置文件的思想相同,你們自行翻閱。

此時全部的Bean定義信息都被保存到了BeanDefinitionRegistry接口,而後走子類DefaultListableBeanFactory工廠的註冊方法

阿里三面:說說你對Spring ioc啓動原理的理解!

3.3.3 prepareBeanFactory(beanFactory)

爲BeanFactory準備一些環境,方便在實例化的時候使用,同時添加容器本身的BeanPostProcessor

阿里三面:說說你對Spring ioc啓動原理的理解!

3.3.4 postProcessBeanFactory

留給子類擴展的BeanFactoryPostProcessor,

3.3.5 invokeBeanFactoryPostProcessors(beanFactory)

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

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

它的整體執行流程是:先執行BeanDefinitionRegistryPostProcessor的BeanFactoryPostProcessor,而後再執行BeanFactoryPostProcessor

下圖是BeanDefinitionRegistryPostProcessor接口的處理過程

阿里三面:說說你對Spring ioc啓動原理的理解!

BeanFactoryPostProcessor的處理邏輯

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

3.3.6 registerBeanPostProcessors

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

阿里三面:說說你對Spring ioc啓動原理的理解!

首先拿到工廠中全部的BeanPostProcessor類型的Bean,而後分類處理,排序註冊。

3.3.7 initMessageSource()

執行國際化內容

3.3.8 initApplicationEventMulticaster

建立了一個多播器,爲添加Listener提供支持。

主要邏輯:

  • 容器中是否存在applicationEventMulticaster,若是存在直接註冊
  • 若是不存在,建立一個SimpleApplicationEventMulticaster,註冊到容器中。

3.3.9 onRefresh()

子類擴展

3.3.10 registerListeners()

觀察者模式的實現

阿里三面:說說你對Spring ioc啓動原理的理解!

3.3.11 finishBeanFactoryInitialization

這一部分的內容太多了,因此採用代碼和圖解的方式來說解。

阿里三面:說說你對Spring ioc啓動原理的理解!

下圖是建立Bean的主要流程

阿里三面:說說你對Spring ioc啓動原理的理解!

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

  1. BeanDefinition是否須要合併。BeanDefinition根據不一樣類型的配置文件信息,會將Bean封裝到不一樣的Bean信息定義類中。好比咱們經常使用的配置文件版的GenericBeanDefinition;註解掃描版的ScannedGenericBeanDefinition等等。

而在這個過程當中就出現了,父定義和子定義,咱們須要在實際處理定義信息的時候進行合併處理,主要有如下三個方面

  • 存在父定義信息,使用父定義信息建立一個RootBeanDefinition,而後將自定義信息做爲參數傳入。
  • 不存在父定義信息,而且當前BeanDefinition是RootBeanDefintion類型的,直接返回一份RootBeanDefintion的克隆
  • 不存在父定義信息,而且當前BeanDefintion不是RootBeanDefintiton類型的,直接經過該BeanDefintion構建一個RootBeanDefintion返回

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

阿里三面:說說你對Spring ioc啓動原理的理解!

  1. isFactoryBean。判斷是否爲FactoryBean

簡單介紹一下:FactoryBean是讓開發者建立本身須要Bean接口。內部提供了三個方法

阿里三面:說說你對Spring ioc啓動原理的理解!

當咱們經過GetBean直接該Bean的時候,獲取到的是該工廠指定返回的Bean類型。若是想要獲取該Bean自己,須要經過一個前綴得到&

阿里三面:說說你對Spring ioc啓動原理的理解!

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

阿里三面:說說你對Spring ioc啓動原理的理解!

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

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

阿里三面:說說你對Spring ioc啓動原理的理解!

在發生循環引用的時候,它首先經過ObejctFactory工廠將Bean建立出來,此時的對象並無進行屬性賦值,僅僅在堆中開闢了空間。而後將此時的Bean添加到earlySingletonObjects容器裏,也就是說這個容器中保存的Bean都是半成品。而在以後的屬性賦值中,因爲對象爲單例的,因此其引用地址不會發生變化,即對象最終是完整的。

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

阿里三面:說說你對Spring 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啓動原理的理解!

這個方法主要完成的事情是:經過Bean的名字拿到對應的Class對象;若是當前Bean獲取到的Class對象不爲空且該RootDefintiton能夠直接獲取到該Bean,克隆一份Bean定義信息,方便以後使用。

驗證當前Bean上的@Override信息。執行BeanPostProcessor,返回一個代理對象(若是存在代理的話), 若是不存在代理,則直接建立Bean

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

阿里三面:說說你對Spring ioc啓動原理的理解!

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

阿里三面:說說你對Spring ioc啓動原理的理解!

後置處理器就不看了,就調用了全部的後置處理器,而後執行了一遍,沒有其餘邏輯。

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

阿里三面:說說你對Spring ioc啓動原理的理解!

其大體流程如上圖:

先判斷之後是否單例,而後從FactoryBean緩存中看一下是否存在正在建立的Bean,若是存在拿出,若是不存在則建立一個當前Bean的包裝類實例。而後拿到這個類的實例和實例類型,執行之後後置處理器。

當前Bean是否爲單例,是否容許循環依賴,時候正在進行建立,若是是,建立一個當前Bean的ObejctFactory以解決循環依賴的問題

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

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

先看循環依賴吧

阿里三面:說說你對Spring ioc啓動原理的理解!

接着來,createBeanInstance

阿里三面:說說你對Spring ioc啓動原理的理解!

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

  • 經過供給者對象對象直接建立。obtainFromSupplier
  • 經過工廠方法直接建立。
  • 默認建立。構造方法是否須要自動注入構造方法不須要自動注入,調用默認的構造方法

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

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

這個方法執行邏輯:

  • 首先判斷傳入的Bean是否爲null,若是爲null則判斷Bean定義信息中是否存在屬性值,若是存在,異常;若是不存在跳過
  • 當前Bean定義信息是否爲合併之後的,若是是且此時的工廠中存在InstantiationAwareBeanPostProcessors,那麼在屬性填充以前進行修改Bean的信息
  • 拿到全部的屬性值,解析屬性值的自動注入方式,Type或者Name,進行自動注入
  • 判斷是否存在InstantiationAwareBeanPostProcessors,修改以前設置的屬性
  • 判斷是否存在依賴檢查,檢查依賴
  • 屬性賦值

阿里三面:說說你對Spring ioc啓動原理的理解!

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

阿里三面:說說你對Spring ioc啓動原理的理解!

這個就是這個方法的執行流程圖,相信到這個地方,你們應該對於爲何BeanPostProcessor的before方法會在init方法執行了解了。這個方法的做用僅僅是用來進行一個生命週期的打印,對象在以前已經建立了。


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

對於單例Bean來講,Spring將須要銷燬的Bean存放到了disposableBeans緩存中,經過DisposableBeanAdapter封裝了銷燬Bean

對於其餘做用域來講,自定義了銷燬回調函數,不過最後仍是封裝爲DisposableBeanAdapter

在封裝爲DisposableBeanAdapter的過程當中,會首先判斷該Bean中是否存在destroy方法,而後給賦值給destroyMethodName變量。再次判斷這個方法的參數,若是參數的個數大於1,則拋出異常

阿里三面:說說你對Spring ioc啓動原理的理解!

3.3.12 finishRefresh

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

阿里三面:說說你對Spring ioc啓動原理的理解!

initLifecycleProcessor,這個方法極具簡單,就看一下當前Bean中是否存在生命週期處理器,若是存在直接使用這個,若是不存在則建立一個默認的,而且註冊爲一個單例的扔到容器中,今日份讀者福利:轉發+關注 獲取小編整理好的微服務全家桶學習筆記!

最後

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

相關文章
相關標籤/搜索