Spring啟動流程
- 2019 年 10 月 3 日
- 筆記
Spring用了挺久的了,但是沒系統做過總結,剛好前段時間在做一個Spring封裝的項目,趁機回顧了下,便基於Spring framework 4.3.22做了源碼分析。
剛開始接觸Spring時的入門例子大致如下:
設置配置文件路徑,初始化ApplicationContext然後獲取Bean,處理完後關閉context即可。這一節先來了解Spring的啟動過程。
一. 啟動
跟蹤ClassPathXmlApplicationContext的構造方法可以看到如下內容:
裏面設置了配置文件的路徑,並且調用了父類AbstractApplicationContext的refresh方法,該方法完成了Spring環境的初始化。如下,為refresh方法的主要過程(為方便排版,去除了原來的注釋和空格):
下面將介紹各個方法步驟的內容,但不進行過多的深入,後面會單獨對每個深入的細節進行詳細的介紹,這節先介紹大概過程。
1.1. try
1.prepareRefresh
PrepareRefresh的內容如上,該方法主要進行環境的準備,包括Context的啟動時間,活動狀態,然後會設置context中的配置數據源,使用默認的StandardEnvironment對象,該對象添加了System.env()屬性和System.properties()屬性。initPropertySources方法用於初始化context 中 environment的屬性源。在AbstractApplicationContext中為空實現。其他子類的實現如下:
對於GenericWebApplicationContext和AbstractRefreshableWebApplicationContext的實現大致一致,都是:
通過在getEnvironment方法中,重寫createEnvironment方法,將默認的StandardEnvironment替換為StandardServletEnvironment, Environment的關係圖為:
因而會執行該類的initPropertySources方法,為context添加ServletContext和ServletConfig對應的配置屬性源。具體的Environment中配置屬性源的加載會在後面單獨進行介紹。
2.obtainFreshBeanFactory
該方法的實現如下,通過refreshBeanFacotry重置AbstractApplicationContext持有的BeanFacotry,然後通過getBeanFacotry獲得該對象再返回。
AbstractApplicationContext中refreshBeanFacoty方法和getBeanFactory方法都是抽象方法,具體實現在AbstractRefreshableApplicationContext上。
如上,增加了方法的注釋,重點在於loadBeanDefinitions方法,該抽象方法在具體實現子類上用於處理不同場景下Bean定義的加載,如Xml配置,註解配置,Web環境等,具體實現會在後面展開。
目前,只是完成了Bean定義的加載,沒有出現Bean的實例化。
3.prepareBeanFactory
為第2步返回的BeanFactory設置基礎屬性。包括:
-
設置ClassLoader
-
設置beanFactory的表達式語言處理器,默認使用EL表達式,可以使用#{bean.xxx}的形式來調用相關屬性值
-
添加默認的屬性編輯器
-
添加後置處理器ApplicationContextAwareProcessor,在Bean初始化後自動執行各Aware接口的set方法,包括ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware、EnvironmentAware
-
添加需要忽略的依賴注入類型,這些類型會在ApplicationContextAwareProcessor中通過BeanPostProcessor後置處理,包括第(4)點涉及的各內容
-
預先設置用於自動依賴注入的接口對象,包括BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext
-
如果存在loadTimeWeaver這個Bean,則增加對應的後置處理器
-
如果不存在environment,systemProperties,systemEnvironment這3個默認的環境屬性Bean,則註冊對應的單例,這3個對象已經在第1步中初始化完成
具體可以看源碼,這步主要預先設置公共的單例Bean並添加一些公共的後置處理動作,主要體現在BeanPostProcessor上。
4.postProcessBeanFactory
所有Bean的定義已經加載完成,但是沒有實例化,這一步可以修改bean定義或者增加自定義的bean,AbstractApplicationContext中為空實現。
如上,以AbstractRefreshableWebApplicationContext為例,其增加了ServletContextAwareProcessor後置處理器,用於處理ServletContextAware接口和ServletConfigAware接口中相關對象的自動注入。同時新增了Web相關的應用範圍,包括:request,session,globalSession和application,並增加了各範圍默認的單例對象。最後增加了Web環境相關的環境配置Bean,包括servletContext,servletConfig,contextParameters和contextAttributes。
該步驟的功能同第3步類似,都能夠增加一些後置處理器。
5.invokeBeanFactoryPostProcessors
在Spring容器中找出實現了BeanFactoryPostProcessor接口的Bean並執行。Spring容器會委託給PostProcessorRegistrationDelegate的invokeBeanFactoryPostProcessors方法執行,內容如下:
invokeBeanFactoryPostProcessors在處理時,將BeanFactoryPostProcessor分為了兩類進行處理,BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor,其中BeanDefinitionRegistryPostProcessor繼承自BeanFactoryPostProcessor。執行的時候,先找出所有的BeanDefinitionRegistryPostProcessor執行再找出所有BeanFactoryPostProcessor執行。因為BeanDefinitionRegistryPostProcessor繼承自BeanFactoryPostProcessor,所以執行後者時會過濾掉前者的內容。
在執行BeanDefinitionRegistryPostProcessor時,會按照如下的優先級,分類先執行postProcessBeanDefinitionRegistry方法,再統一執行所有的postProcessBeanFactory方法,,規則為:
-
篩選實現了PriorityOrdered接口的BeanDefinitionRegistryPostProcessor實現
-
篩選實現了Ordered接口的BeanDefinitionRegistryPostProcessor實現,並執行
-
執行其他BeanDefinitionRegistryPostProcessors
在執行BeanFactoryPostProcessor也會按照如上的規則,執行BeanFactoryPostProcessor方法。
這裡會實例化並初始化實現BeanFactoryPostProcessor接口的類並執行,若存在依賴的的Bean也會被初始化和實例化,具體的過程會在介紹Bean初始化過程時說明。
6.registerBeanPostProcessors
從Spring容器中找出的BeanPostProcessor接口的Bean,並添加到BeanFactory內部維護的List屬性中,以便後續Bean被實例化的時候調用這個BeanPostProcessor進行回調處理。該方法委託給了PostProcessorRegistrationDelegate類的registerBeanPostProcessors方法執行。執行過程同步驟5類似,也是按照優先級進行了篩選,具體順序為:
-
將實現PriorityOrdered接口的BeanPostProcessor列表註冊到ApplicationContext中
-
將實現Ordered接口的BeanPostProcessor列表註冊到ApplicationContext中
-
將剩餘的BeanPostProcessor列表註冊到ApplicationContext中
-
將實現MergedBeanDefinitionPostProcessor接口的BeanPostProcessor列表註冊到ApplicationContext中
其中MergedBeanDefinitionPostProcessor接口繼承自BeanPostProcessor接口,因而,上述第(4)點的列表同頭三點的列表是存在交集的。但是,AbstraceApplicationContext在添加BeanPostProcessor時,會先將存在的對象刪除,再添加新的,如下:
因而執行順序為因而執行順序為: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方法。該方法分為兩部分:
-
遍歷已經解析出來的所有beanDefinitionNames,如果不是抽象類、是單例且沒有設置懶加載,則進行實例化和初始化。
-
在spring容器管理的所有單例對象(非懶加載對象)初始化完成之後調用SmartInitializingSingleton回調接口,注意,該回調只會發生在啟動階段,後續懶加載對象再初始化的話,不會再進行回調
-
finishRefresh
刷新後的其他動作,包括:
-
初始化生命周期處理器DefaultLifecycleProcessor,該處理器管理所有實現了Lifecycle接口的類
-
通知所有Lifecycle.onRefresh,該方法內部調用LifecycleProcessor.startBeans(false),這裡只會調用實現了SmartLifecycle接口,並且設定了AutoStartup的實例,回調將按照設定的優先級,從低到高執行
-
發佈ContextRefreshedEvent通知事件
-
調用LiveBeansView的registerApplicationContext方法
1.2. catch
1.destroyBeans
銷毀所有已經註冊的單例,對於實現了DisposableBean的類,會先單獨進行銷毀,以便執行回調方法,再清理所有單例的緩存信息和剩餘的單例實例
2.cancelRefresh
將當前的活動狀態標識為false
1.3. finally
- resetCommonCaches
清除緩存
二. 關閉
AbstractApplicationContext的close方法如下:
主要是調用doClose方法,然後判斷是否有shutdownHook,如果有則移除該鉤子,避免重複關閉,因為默認的shutdownHook也是調用的doClose方法。
doClose方法如下:
過程為:
-
去除當前Context的MBean,如果開啟了MBean
-
發送ContextClosedEvent通知事件
-
回調聲明周期管理器的onClose方法
-
銷毀已經實例化的單例,同上面提到的一致
-
重置BeanFacotry id為空
-
調用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的實現類。
四. 擴展接口順序
我們知道Spring中存在很多預設的接口,用於擴展。通過以上分析,目前得到的回調接口順序如下:
後續對其他細節進行展開時,會看到更多的擴展接口,到時再更新上面的圖。
個人公眾號:啊駝