Spring啟動流程

  • 2019 年 10 月 3 日
  • 筆記

 Spring用了挺久的了,但是沒系統做過總結,剛好前段時間在做一個Spring封裝的項目,趁機回顧了下,便基於Spring framework 4.3.22做了源碼分析。

 剛開始接觸Spring時的入門例子大致如下:

file

 設置配置文件路徑,初始化ApplicationContext然後獲取Bean,處理完後關閉context即可。這一節先來了解Spring的啟動過程。

一. 啟動

 跟蹤ClassPathXmlApplicationContext的構造方法可以看到如下內容:

file

 裏面設置了配置文件的路徑,並且調用了父類AbstractApplicationContext的refresh方法,該方法完成了Spring環境的初始化。如下,為refresh方法的主要過程(為方便排版,去除了原來的注釋和空格):

file

 下面將介紹各個方法步驟的內容,但不進行過多的深入,後面會單獨對每個深入的細節進行詳細的介紹,這節先介紹大概過程。

1.1. try
1.prepareRefresh

file

 PrepareRefresh的內容如上,該方法主要進行環境的準備,包括Context的啟動時間,活動狀態,然後會設置context中的配置數據源,使用默認的StandardEnvironment對象,該對象添加了System.env()屬性和System.properties()屬性。initPropertySources方法用於初始化context 中 environment的屬性源。在AbstractApplicationContext中為空實現。其他子類的實現如下:

file

 對於GenericWebApplicationContext和AbstractRefreshableWebApplicationContext的實現大致一致,都是:

file

 通過在getEnvironment方法中,重寫createEnvironment方法,將默認的StandardEnvironment替換為StandardServletEnvironment, Environment的關係圖為:

file

 因而會執行該類的initPropertySources方法,為context添加ServletContext和ServletConfig對應的配置屬性源。具體的Environment中配置屬性源的加載會在後面單獨進行介紹。

2.obtainFreshBeanFactory

 該方法的實現如下,通過refreshBeanFacotry重置AbstractApplicationContext持有的BeanFacotry,然後通過getBeanFacotry獲得該對象再返回。

file

 AbstractApplicationContext中refreshBeanFacoty方法和getBeanFactory方法都是抽象方法,具體實現在AbstractRefreshableApplicationContext上。

file

 如上,增加了方法的注釋,重點在於loadBeanDefinitions方法,該抽象方法在具體實現子類上用於處理不同場景下Bean定義的加載,如Xml配置,註解配置,Web環境等,具體實現會在後面展開。

 目前,只是完成了Bean定義的加載,沒有出現Bean的實例化。

3.prepareBeanFactory

 為第2步返回的BeanFactory設置基礎屬性。包括:

  1. 設置ClassLoader

  2. 設置beanFactory的表達式語言處理器,默認使用EL表達式,可以使用#{bean.xxx}的形式來調用相關屬性值

  3. 添加默認的屬性編輯器

  4. 添加後置處理器ApplicationContextAwareProcessor,在Bean初始化後自動執行各Aware接口的set方法,包括ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware、EnvironmentAware

  5. 添加需要忽略的依賴注入類型,這些類型會在ApplicationContextAwareProcessor中通過BeanPostProcessor後置處理,包括第(4)點涉及的各內容

  6. 預先設置用於自動依賴注入的接口對象,包括BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext

  7. 如果存在loadTimeWeaver這個Bean,則增加對應的後置處理器

  8. 如果不存在environment,systemProperties,systemEnvironment這3個默認的環境屬性Bean,則註冊對應的單例,這3個對象已經在第1步中初始化完成

 具體可以看源碼,這步主要預先設置公共的單例Bean並添加一些公共的後置處理動作,主要體現在BeanPostProcessor上。

4.postProcessBeanFactory

 所有Bean的定義已經加載完成,但是沒有實例化,這一步可以修改bean定義或者增加自定義的bean,AbstractApplicationContext中為空實現。

file

 如上,以AbstractRefreshableWebApplicationContext為例,其增加了ServletContextAwareProcessor後置處理器,用於處理ServletContextAware接口和ServletConfigAware接口中相關對象的自動注入。同時新增了Web相關的應用範圍,包括:request,session,globalSession和application,並增加了各範圍默認的單例對象。最後增加了Web環境相關的環境配置Bean,包括servletContext,servletConfig,contextParameters和contextAttributes。

 該步驟的功能同第3步類似,都能夠增加一些後置處理器。

5.invokeBeanFactoryPostProcessors

 在Spring容器中找出實現了BeanFactoryPostProcessor接口的Bean並執行。Spring容器會委託給PostProcessorRegistrationDelegate的invokeBeanFactoryPostProcessors方法執行,內容如下:

file

 invokeBeanFactoryPostProcessors在處理時,將BeanFactoryPostProcessor分為了兩類進行處理,BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor,其中BeanDefinitionRegistryPostProcessor繼承自BeanFactoryPostProcessor。執行的時候,先找出所有的BeanDefinitionRegistryPostProcessor執行再找出所有BeanFactoryPostProcessor執行。因為BeanDefinitionRegistryPostProcessor繼承自BeanFactoryPostProcessor,所以執行後者時會過濾掉前者的內容。

 在執行BeanDefinitionRegistryPostProcessor時,會按照如下的優先級,分類先執行postProcessBeanDefinitionRegistry方法,再統一執行所有的postProcessBeanFactory方法,,規則為:

  1. 篩選實現了PriorityOrdered接口的BeanDefinitionRegistryPostProcessor實現

  2. 篩選實現了Ordered接口的BeanDefinitionRegistryPostProcessor實現,並執行

  3. 執行其他BeanDefinitionRegistryPostProcessors

 在執行BeanFactoryPostProcessor也會按照如上的規則,執行BeanFactoryPostProcessor方法。

這裡會實例化並初始化實現BeanFactoryPostProcessor接口的類並執行,若存在依賴的的Bean也會被初始化和實例化,具體的過程會在介紹Bean初始化過程時說明。

6.registerBeanPostProcessors

 從Spring容器中找出的BeanPostProcessor接口的Bean,並添加到BeanFactory內部維護的List屬性中,以便後續Bean被實例化的時候調用這個BeanPostProcessor進行回調處理。該方法委託給了PostProcessorRegistrationDelegate類的registerBeanPostProcessors方法執行。執行過程同步驟5類似,也是按照優先級進行了篩選,具體順序為:

  1. 將實現PriorityOrdered接口的BeanPostProcessor列表註冊到ApplicationContext中

  2. 將實現Ordered接口的BeanPostProcessor列表註冊到ApplicationContext中

  3. 將剩餘的BeanPostProcessor列表註冊到ApplicationContext中

  4. 將實現MergedBeanDefinitionPostProcessor接口的BeanPostProcessor列表註冊到ApplicationContext中

 其中MergedBeanDefinitionPostProcessor接口繼承自BeanPostProcessor接口,因而,上述第(4)點的列表同頭三點的列表是存在交集的。但是,AbstraceApplicationContext在添加BeanPostProcessor時,會先將存在的對象刪除,再添加新的,如下:

file

 因而執行順序為因而執行順序為:PriorityOrdered、Ordered、NotOrdered、MergedBeanDefinitionPostProcessor。

這裡會實例化並初始化實現BeanPostProcessor接口的類,但不執行,若存在依賴的的Bean也會被初始化和實例化。

7.initMessageSource

 在Spring容器中初始化一些國際化相關的屬性

8.initApplicationEventMulticaster

 在Spring容器中初始化事件廣播器對象SimpleApplicationEventMulticaster,並將該對象作為單例applicationEventMulticaster註冊到Context中。該廣播器用於廣播ApplicationEvent事件對應的ApplicationListener接口Bean。

PS:根據以上的順序,在這之前實例化的Bean,都不會經過BeanFactoryPostProcessor和BeanPostProcessor的處理,包括因為依賴而實例化的Bean,還有提前通過new註冊的Bean(只有直接調用BeanFactory.getBean方法獲取的bean才會進行後置回調)。這裡需要注意,Context提前將兩種後置處理器的所有實現都提前加載了,由於實例化前需要將依賴的Bean提前實例化,所以被這兩種後置處理器依賴的Bean的初始化動作,是不會被其監聽到的。

9.onRefresh

 模板方法,可用於refresh動作的擴展,默認為空實現。在SpringBoot中主要用於啟動內嵌的web服務器。

10.registerListeners

 找出系統中的ApplicationListener對象,註冊到時間廣播器中。如果有需要提前進行廣播的時間,則執行廣播.

11.finishBeanFactoryInitialization

 實例化BeanFactory中已經被註冊但是未實例化的所有實例(懶加載的不需要實例化),主要操作是BeanFacotry的preInstantiateSingletons方法。該方法分為兩部分:

  1. 遍歷已經解析出來的所有beanDefinitionNames,如果不是抽象類、是單例且沒有設置懶加載,則進行實例化和初始化。

  2. 在spring容器管理的所有單例對象(非懶加載對象)初始化完成之後調用SmartInitializingSingleton回調接口,注意,該回調只會發生在啟動階段,後續懶加載對象再初始化的話,不會再進行回調

  3. finishRefresh

 刷新後的其他動作,包括:

  1. 初始化生命周期處理器DefaultLifecycleProcessor,該處理器管理所有實現了Lifecycle接口的類

  2. 通知所有Lifecycle.onRefresh,該方法內部調用LifecycleProcessor.startBeans(false),這裡只會調用實現了SmartLifecycle接口,並且設定了AutoStartup的實例,回調將按照設定的優先級,從低到高執行

  3. 發佈ContextRefreshedEvent通知事件

  4. 調用LiveBeansView的registerApplicationContext方法

1.2. catch
1.destroyBeans

 銷毀所有已經註冊的單例,對於實現了DisposableBean的類,會先單獨進行銷毀,以便執行回調方法,再清理所有單例的緩存信息和剩餘的單例實例

2.cancelRefresh

 將當前的活動狀態標識為false

1.3. finally
  1. resetCommonCaches

 清除緩存

二. 關閉

 AbstractApplicationContext的close方法如下:

file

 主要是調用doClose方法,然後判斷是否有shutdownHook,如果有則移除該鉤子,避免重複關閉,因為默認的shutdownHook也是調用的doClose方法。

 doClose方法如下:

file

 過程為:

  1. 去除當前Context的MBean,如果開啟了MBean

  2. 發送ContextClosedEvent通知事件

  3. 回調聲明周期管理器的onClose方法

  4. 銷毀已經實例化的單例,同上面提到的一致

  5. 重置BeanFacotry id為空

  6. 調用onClose方法,默認實現為空,對於SpringBoot應用,在前面說過,SpringBoot應用重寫了onRefresh方法用於啟動web服務器,而在這裡,則用於關閉內嵌的web服務器。(PS:注意這裡,web服務器的關閉是在所有Bean銷毀後再關閉的,因而在關閉服務器前,web還會接收Http請求,有可能導致請求無法處理,官方給了一個解決方法,詳見issue https://github.com/spring-projects/spring-boot/issues/4657

三. Start Stop方法

 這兩個方法來自Lifecycle接口,如下,簡單的調用了DefaultLifecycleProcessor的start和stop方法,回調Lifecycle的實現類。

file

file

四. 擴展接口順序

 我們知道Spring中存在很多預設的接口,用於擴展。通過以上分析,目前得到的回調接口順序如下:

file

 後續對其他細節進行展開時,會看到更多的擴展接口,到時再更新上面的圖。

file

個人公眾號:啊駝