springboot啓動原理分析

在 Spring Boot 項目的啓動類中常見代碼如下:

一、SpringApplication 初始化

探究SpringApplication.run() 的實現

在這個靜態方法中,創建 SpringApplication 對象,並調用該對象的 run 方法。

首先是進入單個參數的構造方法,然後進入兩參數的構造方法(ResourceLoader 爲 null),然後進行初始化。

1、deduceWebApplicationType() : 推斷應用的類型

判斷創建的是一個 SERVLET 應用還是 REACTIVE應用或者是 NONE

2、初始化 classpath 下的所有的可用的 ApplicationContextInitializer

setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class))

1)、getSpringFactoriesInstances()

上面的 SpringFactoriesLoader.loadFactoryNames() ,是從 META-INF/spring.factories 的資源文件中,讀取 key 爲org.springframework.context.ApplicationContextInitializer 的 value。

而 spring.factories 的部分內容如下:

2)、setInitializers():

所以,這裏 setInitializers() 所得到的成員變量 initializers 就被初始化爲ConfigurationWarningsApplicationContextInitializer,ContextIdApplicationContextInitializer,DelegatingApplicationContextInitializer,ServerPortInfoApplicationContextInitializer 這四個類的對象組成的 list。

3、初始化 classpath 下的所有的可用的 ApplicationListener

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)):

1)、getSpringFactoriesInstances()

和上面的類似,但是它是從 META-INF/spring.factories 的資源文件中,獲取到 key 爲 org.springframework.context.ApplicationListener 的 value。

2)、setListeners():

所以,這裏 setListeners() 所得到的成員變量 listeners 就被初始化爲 ClearCachesApplicationListener,ParentContextCloserApplicationListener,FileEncodingApplicationListener,AnsiOutputApplicationListener ,ConfigFileApplicationListener,DelegatingApplicationListener,ClasspathLoggingApplicationListener,LoggingApplicationListener,LiquibaseServiceLocatorApplicationListener 這九個類的對象組成的 list。

4、deduceMainApplicationClass() :根據調用棧,推斷出 main 方法的類名

二、run 方法解析

上面看完了構造方法後,已經初始化了一個 SpringApplication 對象,接下來調用其 run 方法,代碼如下:

可變個數參數 args 即是我們整個應用程序的入口 main 方法的參數。StopWatch 是來自 org.springframework.util 的工具類,可以用來方便的記錄程序的運行時間。

1、configureHeadlessProperty():設置 headless 模式

Headless模式是系統的一種配置模式。在該模式下,系統缺少了顯示設備、鍵盤或鼠標。
Headless模式雖然不是我們願意見到的,但事實上我們卻常常需要在該模式下工作,尤其是服務器端程序開發者。因爲服務器(如提供Web服務的主機)往往可能缺少前述設備,但又需要使用他們提供的功能,生成相應的數據,以提供給客戶端(如瀏覽器所在的配有相關的顯示設備、鍵盤和鼠標的主機)。

實際上是就是設置系統屬性 java.awt.headless,該屬性會被設置爲 true。(System.setProperty("java.awt.headless", "true");)

2、getRunListeners():加載 SpringApplicationRunListener 對象

上面的 getRunListeners() 中也利用 SpringFactoriesLoader 加載 META-INF/spring.factories 中 key 爲 SpringApplicationRunListener 的值,然後再將獲取到的值作爲參數傳遞到 SpringApplicationRunListeners 的構造方法中去創建對象。

3、new DefaultApplicationArguments(args) :獲取啓動時傳入參數 args(main 方法傳進來的參數) 並初始化爲 ApplicationArguments 對象。

4、prepareEnvironment(listeners, applicationArguments):根據 listeners 和 applicationArguments 配置SpringBoot 應用的環境。

5、configureIgnoreBeanInfo(environment):根據環境信息配置要忽略的 bean 信息

6、printBanner(environment):打印標誌。

7、createApplicationContext():根據應用類型來確定該 Spring Boot 項目應該創建什麼類型的 ApplicationContext ,默認情況下,如果沒有明確設置的應用程序上下文或應用程序上下文類,該方法會在返回合適的默認值。

8、exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context)

獲取異常,這裏也是通過 SpringFactoriesLoader 加載 META-INF/spring.factories 中 key 爲 SpringBootExceptionReporter 的全類名的 value 值。

9、prepareContext(context, environment, listeners, applicationArguments, printedBanner):完成整個容器的創建與啓動以及 bean 的注入功能。

1)、postProcessApplicationContext(context)

該方法對 context 進行了預設置,設置了 ResourceLoader 和 ClassLoader,並向 bean 工廠中添加了一個beanNameGenerator 。

2)、applyInitializers(context)

在刷新之前將任何 ApplicationContextInitializer 應用於上下文

3)、load(context, sources.toArray(new Object[0]))

主要是加載各種 beans 到 ApplicationContext 對象中。

(1)、getBeanDefinitionRegistry(context)

獲取 bean 定義註冊表

(2)、createBeanDefinitionLoader()

通過 BeanDefinitionLoader 的構造方法把參數(註冊表、資源)傳進去,然後創建 BeanDefinitionLoader。

(3)、load()

把資源全部加載。

10、refreshContext(context)

refreshContext(context) 方法又調用了 refresh(context)。在調用了 refresh(context) 方法之後,調用了 registerShutdownHook 方法:

進入refresh方法:可以從註釋看到,正在做各種初始化工作

進入onRefresh() 方法,查找它的父類ReactiveWebServerApplicationContext 的實現,可以看到此時正在創建web容器,先判斷是否存在本地的容器,然後再使用自動配置的容器,以tomcat爲例,進入TomcatReactiveWebServerFactory:

方法 finishBeanFactoryInitialization(beanFactory)進行了非懶加載 beans 的初始化工作。

11、afterRefresh(context, applicationArguments):在上下文刷新後調用該方法,其內部沒有做任何操作。

12、輸出日誌記錄執行主類名、時間信息

13、發佈應用上下文啓動完成事件

listeners.started(context);

觸發所有 SpringApplicationRunListener 監聽器的 started 事件方法。

14、執行所有 Runner 運行器

執行所有 ApplicationRunner 和 CommandLineRunner 這兩種運行器。

15、發佈應用上下文就緒事件

listeners.running(context);

觸發所有 SpringApplicationRunListener 監聽器的 running 事件方法。

16、返回應用上下文

return context;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章