一、SpringApplication初始化
SpringBoot整個啟動流程分為兩個步驟:初始化一個SpringApplication對象、執行該對象的run方法。看下SpringApplication的初始化流程,SpringApplication的構造方法中調用initialize(Object[] sources)方法,其代碼如下:
private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
// 判斷是否是Web項目
this.webEnvironment = deduceWebEnvironment();
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 找到入口類
this.mainApplicationClass = deduceMainApplicationClass();
}
初始化流程中最重要的就是通過SpringFactoriesLoader找到
spring.factories
檔案中配置的
ApplicationContextInitializer
和
ApplicationListener
兩個接口的實作類名稱,以便後期構造相應的執行個體。
ApplicationContextInitializer
的主要目的是在
ConfigurableApplicationContext
做refresh之前,對ConfigurableApplicationContext執行個體做進一步的設定或處理。ConfigurableApplicationContext繼承自ApplicationContext,其主要提供了對ApplicationContext進行設定的能力。
實作一個ApplicationContextInitializer非常簡單,因為它隻有一個方法,但大多數情況下我們沒有必要自定義一個ApplicationContextInitializer,即便是Spring Boot架構,它預設也隻是注冊了兩個實作,畢竟Spring的容器已經非常成熟和穩定,你沒有必要來改變它。
而
ApplicationListener
的目的就沒什麼好說的了,它是Spring架構對Java事件監聽機制的一種架構實作,具體内容在前文Spring事件監聽機制這個小節有詳細講解。這裡主要說說,如果你想為Spring Boot應用添加監聽器,該如何實作?
Spring Boot提供兩種方式來添加自定義監聽器:
- 通過
或者SpringApplication.addListeners(ApplicationListener<?>... listeners)
兩個方法來添加一個或者多個自定義監聽器SpringApplication.setListeners(Collection<? extends ApplicationListener<?>> listeners)
- 既然SpringApplication的初始化流程中已經從
中擷取到spring.factories
的實作類,那麼我們直接在自己的jar包的ApplicationListener
檔案中新增配置即可:META-INF/spring.factories
org.springframework.context.ApplicationListener=\
cn.moondev.listeners.xxxxListener\
二、Spring Boot啟動流程
Spring Boot應用的整個啟動流程都封裝在SpringApplication.run方法中,其整個流程真的是太長太長了,但本質上就是在Spring容器啟動的基礎上做了大量的擴充,按照這個思路來看看源碼:
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
// ①
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
// ②
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);
// ③
Banner printedBanner = printBanner(environment);
// ④
context = createApplicationContext();
// ⑤
analyzers = new FailureAnalyzers(context);
// ⑥
prepareContext(context, environment, listeners, applicationArguments,printedBanner);
// ⑦
refreshContext(context);
// ⑧
afterRefresh(context, applicationArguments);
// ⑨
listeners.finished(context, null);
stopWatch.stop();
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
① 通過SpringFactoriesLoader查找并加載所有的
SpringApplicationRunListeners
,通過調用starting()方法通知所有的SpringApplicationRunListeners:應用開始啟動了。SpringApplicationRunListeners其本質上就是一個事件釋出者,它在SpringBoot應用啟動的不同時間點釋出不同應用事件類型(ApplicationEvent),如果有哪些事件監聽者(ApplicationListener)對這些事件感興趣,則可以接收并且處理。還記得初始化流程中,SpringApplication加載了一系列ApplicationListener嗎?這個啟動流程中沒有發現有釋出事件的代碼,其實都已經在SpringApplicationRunListeners這兒實作了。
簡單的分析一下其實作流程,首先看下SpringApplicationRunListener的源碼:
public interface SpringApplicationRunListener {
// 運作run方法時立即調用此方法,可以使用者非常早期的初始化工作
void starting();
// Environment準備好後,并且ApplicationContext建立之前調用
void environmentPrepared(ConfigurableEnvironment environment);
// ApplicationContext建立好後立即調用
void contextPrepared(ConfigurableApplicationContext context);
// ApplicationContext加載完成,在refresh之前調用
void contextLoaded(ConfigurableApplicationContext context);
// 當run方法結束之前調用
void finished(ConfigurableApplicationContext context, Throwable exception);
}
SpringApplicationRunListener隻有一個實作類:
EventPublishingRunListener
。上面的代碼隻會擷取到一個EventPublishingRunListener的執行個體,我們來看看starting()方法的内容:
public void starting() {
// 釋出一個ApplicationStartedEvent
this.initialMulticaster.multicastEvent(new ApplicationStartedEvent(this.application, this.args));
}
順着這個邏輯,你可以在
prepareEnvironment()
方法的源碼中找到
listeners.environmentPrepared(environment);
即SpringApplicationRunListener接口的第二個方法,那不出你所料,
environmentPrepared()
又釋出了另外一個事件
ApplicationEnvironmentPreparedEvent
。接下來會發生什麼,就不用我多說了吧。
② 建立并配置目前應用将要使用的
Environment
,Environment用于描述應用程式目前的運作環境,其抽象了兩個方面的内容:配置檔案(profile)和屬性(properties),開發經驗豐富的同學對這兩個東西一定不會陌生:不同的環境(eg:生産環境、預釋出環境)可以使用不同的配置檔案,而屬性則可以從配置檔案、環境變量、指令行參數等來源擷取。是以,當Environment準備好後,在整個應用的任何時候,都可以從Environment中擷取資源。
總結起來,主要完成以下幾件事:
- 判斷Environment是否存在,不存在就建立(如果是web項目就建立
,否則建立StandardServletEnvironment
)StandardEnvironment
- 配置Environment:配置profile以及properties
- 調用SpringApplicationRunListener的
方法,通知事件監聽者:應用的Environment已經準備好environmentPrepared()
③、SpringBoot應用在啟動時會輸出這樣的東西:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.5.6.RELEASE)
④、根據是否是web項目,來建立不同的ApplicationContext容器。
⑤、建立一系列
FailureAnalyzer
,建立流程依然是通過SpringFactoriesLoader擷取到所有實作FailureAnalyzer接口的class,然後在建立對應的執行個體。FailureAnalyzer用于分析故障并提供相關診斷資訊。
⑥、初始化ApplicationContext,主要完成以下工作:
- 将準備好的Environment設定給ApplicationContext
- 周遊調用所有的ApplicationContextInitializer的
方法來對已經建立好的ApplicationContext進行進一步的處理initialize()
- 調用SpringApplicationRunListener的
方法,通知所有的監聽者:ApplicationContext已經準備完畢contextPrepared()
- 将所有的bean加載到容器中
- 調用SpringApplicationRunListener的
方法,通知所有的監聽者:ApplicationContext已經裝載完畢contextLoaded()
⑦、調用ApplicationContext的
refresh()
方法,完成IoC容器可用的最後一道工序。從名字上了解為重新整理容器,那何為重新整理?就是插手容器的啟動,聯系一下第一小節的内容。那如何重新整理呢?且看下面代碼:
// 摘自refresh()方法中一句代碼
invokeBeanFactoryPostProcessors(beanFactory);
看看這個方法的實作:
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
......
}
擷取到所有的
BeanFactoryPostProcessor
來對容器做一些額外的操作。BeanFactoryPostProcessor允許我們在容器執行個體化相應對象之前,對注冊到容器的BeanDefinition所儲存的資訊做一些額外的操作。這裡的getBeanFactoryPostProcessors()方法可以擷取到3個Processor:
ConfigurationWarningsApplicationContextInitializer$ConfigurationWarningsPostProcessor
SharedMetadataReaderFactoryContextInitializer$CachingMetadataReaderFactoryPostProcessor
ConfigFileApplicationListener$PropertySourceOrderingPostProcessor
不是有那麼多BeanFactoryPostProcessor的實作類,為什麼這兒隻有這3個?因為在初始化流程擷取到的各種ApplicationContextInitializer和ApplicationListener中,隻有上文3個做了類似于如下操作:
public void initialize(ConfigurableApplicationContext context) {
context.addBeanFactoryPostProcessor(new ConfigurationWarningsPostProcessor(getChecks()));
}
然後你就可以進入到
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()
方法了,這個方法除了會周遊上面的3個BeanFactoryPostProcessor處理外,還會擷取類型為
BeanDefinitionRegistryPostProcessor
的bean:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
,對應的Class為
ConfigurationClassPostProcessor
。
ConfigurationClassPostProcessor
用于解析處理各種注解,包括:@Configuration、@ComponentScan、@Import、@PropertySource、@ImportResource、@Bean。當處理
@import
注解的時候,就會調用
EnableAutoConfigurationImportSelector.selectImports()
來完成自動配置功能
⑧、查找目前context中是否注冊有CommandLineRunner和ApplicationRunner,如果有則周遊執行它們。
⑨、執行所有SpringApplicationRunListener的finished()方法。
這就是Spring Boot的整個啟動流程,其核心就是在Spring容器初始化并啟動的基礎上加入各種擴充點,這些擴充點包括:ApplicationContextInitializer、ApplicationListener以及各種BeanFactoryPostProcessor等等。你對整個流程的細節不必太過關注,甚至沒弄明白也沒有關系,你隻要了解這些擴充點是在何時如何工作的,能讓它們為你所用即可。
内容轉自:https://www.jianshu.com/p/83693d3d0a65