Spring啓動流程

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

 剛開始接觸Spring時的入門例子大體以下:github

file

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

一. 啓動

 跟蹤ClassPathXmlApplicationContext的構造方法能夠看到以下內容:spring

file

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

file

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

1.1. try
1.prepareRefresh

file

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

file

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

file

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

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

我的公衆號:啊駝

相關文章
相關標籤/搜索