前言
????????最近在學(xué)習(xí)Spring Boot相關(guān)的課程,過程中以筆記的形式記錄下來咸灿,方便以后回憶摸吠,同時也在這里和大家探討探討,文章中有漏的或者有補(bǔ)充的较解、錯誤的都希望大家能夠及時提出來畜疾,本人在此先謝謝了!
開始之前呢印衔,希望大家?guī)е鴰讉€問題去學(xué)習(xí):
1啡捶、Spring Boot SpringApplication 是什么?
2奸焙、整體流程或結(jié)構(gòu)是怎樣的瞎暑?
3、重點(diǎn)內(nèi)容或者核心部分是什么与帆?
4了赌、怎么實(shí)現(xiàn)的?
5玄糟、是怎么和 Spring 關(guān)聯(lián)起來的勿她?
這是對自我的提問,我認(rèn)為帶著問題去學(xué)習(xí)茶凳,是一種更好的學(xué)習(xí)方式嫂拴,有利于加深理解。好了贮喧,接下來進(jìn)入主題筒狠。
1、起源
????????上篇文章我們講了 SpringApplication
的準(zhǔn)備階段箱沦,在這個階段辩恼,完成了運(yùn)行時所需要準(zhǔn)備的資源,如:initializers
谓形、listeners
等灶伊。而這篇文章我們就來講講 SpringApplication
的運(yùn)行階段,在這個階段寒跳,它是如何啟動 Spring
應(yīng)用上下文的聘萨,且如何與 Spring
事件結(jié)合起來,形成完整的 SpringApplication
生命周期的童太。
注:本篇文章所用到的
Spring Boot
版本是2.1.6.BUILD-SNAPSHOT
2米辐、SpringApplication 運(yùn)行階段
????????上篇文章我們講了 SpringApplication
的構(gòu)造方法胸完,這里我們就來講講 SpringApplication
的核心,也就是run方法翘贮,代碼如下:
public class SpringApplication {
...
public ConfigurableApplicationContext run(String... args) {
// 這是 Spring 的一個計(jì)時器赊窥,計(jì)算代碼的執(zhí)行時間(ms級別)
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 這倆變量在后面賦值處進(jìn)行說明
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 用來設(shè)置java.awt.headless屬性值
configureHeadlessProperty();
// 該對象屬于組合模式的實(shí)現(xiàn),核心是內(nèi)部關(guān)聯(lián)的 SpringApplicationRunListener 集合狸页,SpringApplicationRunListener 是 Spring Boot 的運(yùn)行時監(jiān)聽器
SpringApplicationRunListeners listeners = getRunListeners(args);
// 會在不同的階段調(diào)用對應(yīng)的方法锨能,這里表示啟動run方法被調(diào)用
listeners.starting();
try {
// 用來獲取 SpringApplication.run(args)傳入的參數(shù)
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 獲取 properties 配置文件
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 設(shè)置 spring.beaninfo.ignore 的屬性值,判斷是否跳過搜索BeanInfo類
configureIgnoreBeanInfo(environment);
// 這里是項(xiàng)目啟動時芍耘,控制臺打印的 Banner
Banner printedBanner = printBanner(environment);
// 這里就是創(chuàng)建 Spring 應(yīng)用上下文
context = createApplicationContext();
// 獲取 spring.factories 中key為 SpringBootExceptionReporter 的類名集合
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 這里是準(zhǔn)備 Spring 應(yīng)用上下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 這里是啟動 Spring 應(yīng)用上下文址遇,底層調(diào)用的是 ApplicationContext 的 refresh() 方法,到這里就正式進(jìn)入了 Spring 的生命周期齿穗,同時傲隶,SpringBoot的自動裝配特性也隨之啟動
refreshContext(context);
// 里面是空的,猜測應(yīng)該是交由開發(fā)人員自行擴(kuò)展
afterRefresh(context, applicationArguments);
stopWatch.stop();
// 這里打印啟動信息
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// ApplicationContext 啟動時窃页,調(diào)用該方法
listeners.started(context);
// 項(xiàng)目啟動后跺株,做的一些操作,開發(fā)人員可自行擴(kuò)展
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
// ApplicationContext 啟動完成時脖卖,調(diào)用該方法
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
...
}
上面就是整個過程的概覽乒省,可以看到,在運(yùn)行階段執(zhí)行的操作比較多畦木,雖然看起來雜亂無章袖扛,但其實(shí)還是有規(guī)律可循的。比如十籍,執(zhí)行的 SpringApplicationRunListeners
中的階段方法蛆封,剛啟動階段的 starting
、已啟動階段的 started
勾栗、啟動完成階段的 running
等惨篱。還有對應(yīng)的 Spring
應(yīng)用上下文的創(chuàng)建、準(zhǔn)備围俘、啟動操作等砸讳。接下來,就對里面的幾個核心對象進(jìn)行討論界牡。
2.1 SpringApplicationRunListeners 結(jié)構(gòu)
我們先來看看 SpringApplicationRunListeners
對象簿寂,從代碼可以看出該對象是由 getRunListeners
方法創(chuàng)建的:
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
可以看到,通過傳入的 getSpringFactoriesInstances
方法的返回值宿亡,執(zhí)行 SpringApplicationRunListeners
的構(gòu)造方法常遂,進(jìn)行對象的創(chuàng)建。接著看 getSpringFactoriesInstances
方法:
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
看到這大家應(yīng)該比較熟悉了挽荠,通過前面幾篇文章的討論我們知道烈钞,該方法通過 SpringFactoriesLoader.loadFactoryNames
返回所有 classpass 下的 spring.factories
文件中 key 為 SpringApplicationRunListener
的實(shí)現(xiàn)類集合泊碑。如 Spring Boot 的內(nèi)建實(shí)現(xiàn):
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
最后,就是將該集合傳入 SpringApplicationRunListeners
的構(gòu)造方法:
class SpringApplicationRunListeners {
...
private final List<SpringApplicationRunListener> listeners;
SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
this.log = log;
this.listeners = new ArrayList<>(listeners);
}
public void starting() {
for (SpringApplicationRunListener listener : this.listeners) {
listener.starting();
}
}
...
}
里面是將集合賦值到 listeners
屬性毯欣,可以看到 SpringApplicationRunListeners
屬于組合模式的實(shí)現(xiàn),核心其實(shí)是內(nèi)部關(guān)聯(lián)的 SpringApplicationRunListener
對象集合臭脓,當(dāng)外部調(diào)用該階段方法時酗钞,就會迭代執(zhí)行集合中 SpringApplicationRunListener
對應(yīng)的方法。所以接下來我們就來討論 SpringApplicationRunListener
来累。
2.1.1 SpringApplicationRunListener 事件和監(jiān)聽機(jī)制
SpringApplicationRunListener
負(fù)責(zé)在 SpringBoot
的不同階段廣播相應(yīng)的事件砚作,然后調(diào)用實(shí)際的 ApplicationListener
類,在該類的 onApplicationEvent
方法中嘹锁,根據(jù)不同的 Spring Boot
事件執(zhí)行相應(yīng)操作葫录。整個過程大概如此,接下來進(jìn)行詳細(xì)討論领猾,先來看看 SpringApplicationRunListener
定義:
public interface SpringApplicationRunListener {
// 在run()方法開始執(zhí)行時被調(diào)用米同,表示應(yīng)用剛剛啟動,對應(yīng)的 Spring Boot 事件為 ApplicationStartingEvent
void starting();
// ConfigurableEnvironment 構(gòu)建完成時調(diào)用摔竿,對應(yīng)的 Spring Boot 事件為 ApplicationEnvironmentPreparedEvent
void environmentPrepared(ConfigurableEnvironment environment);
// ApplicationContext 構(gòu)建完成時調(diào)用面粮,對應(yīng)的 Spring Boot 事件為 ApplicationContextInitializedEvent
void contextPrepared(ConfigurableApplicationContext context);
// ApplicationContext 完成加載但還未啟動時調(diào)用,對應(yīng)的 Spring Boot 事件為 ApplicationPreparedEvent
void contextLoaded(ConfigurableApplicationContext context);
// ApplicationContext 已啟動继低,但 callRunners 還未執(zhí)行時調(diào)用熬苍,對應(yīng)的 Spring Boot 事件為 ApplicationStartedEvent
void started(ConfigurableApplicationContext context);
// ApplicationContext 啟動完畢被調(diào)用,對應(yīng)的 Spring Boot 事件為 ApplicationReadyEvent
void running(ConfigurableApplicationContext context);
// 應(yīng)用出錯時被調(diào)用袁翁,對應(yīng)的 Spring Boot 事件為 ApplicationFailedEvent
void failed(ConfigurableApplicationContext context, Throwable exception);
}
我們來看看它的實(shí)現(xiàn)類柴底,也就是上面加載的 spring.factories
文件中的 EventPublishingRunListener
類,該類也是 Spring Boot
內(nèi)建的唯一實(shí)現(xiàn)類粱胜,具體廣播事件的操作在該類中進(jìn)行柄驻,代碼如下:
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
private final SpringApplication application;
private final String[] args;
private final SimpleApplicationEventMulticaster initialMulticaster;
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
this.initialMulticaster = new SimpleApplicationEventMulticaster();
for (ApplicationListener<?> listener : application.getListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
@Override
public void starting() {
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
...
}
可以看到,通過構(gòu)造方法創(chuàng)建 EventPublishingRunListener
實(shí)例的過程中年柠,調(diào)用了 getListeners
方法凿歼,將 SpringApplication
中所有 ApplicationListener
監(jiān)聽器關(guān)聯(lián)到了 initialMulticaster
屬性中。沒錯冗恨,這里的 ApplicationListener
監(jiān)聽器就是上篇文章中在 SpringApplication
準(zhǔn)備階段從 spring.factories
文件加載的 key 為 ApplicationListener
的實(shí)現(xiàn)類集合答憔,該實(shí)現(xiàn)類集合全部重寫了 onApplicationEvent
方法。
2.1.2 SimpleApplicationEventMulticaster 廣播器
這里又引出了另一個類掀抹, 也就是 SimpleApplicationEventMulticaster
虐拓,該類是 Spring
的事件廣播器,也就是通過它來廣播各種事件傲武。接著蓉驹,當(dāng)外部迭代的執(zhí)行到 EventPublishingRunListener
的 starting
方法時城榛,會通過 SimpleApplicationEventMulticaster
的 multicastEvent
方法進(jìn)行事件的廣播,這里廣播的是 ApplicationStartingEvent
事件态兴,我們進(jìn)入 multicastEvent
方法:
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
...
@Override
public void multicastEvent(ApplicationEvent event) {
multicastEvent(event, resolveDefaultEventType(event));
}
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
}
通過 getApplicationListeners
方法狠持,根據(jù)事件類型返回從上面關(guān)聯(lián)的 ApplicationListener
集合中篩選出匹配的 ApplicationListener
集合,根據(jù) Spring Boot
版本的不同瞻润,在這個階段獲取到的監(jiān)聽器也有可能不同喘垂,如 2.1.6.BUILD-SNAPSHOT
版本返回的是:
然后依次遍歷這些監(jiān)聽器,同步或異步的調(diào)用 invokeListener
方法:
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
doInvokeListener(listener, event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else {
doInvokeListener(listener, event);
}
}
...
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
String msg = ex.getMessage();
if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
// Possibly a lambda-defined listener which we could not resolve the generic event type for
// -> let's suppress the exception and just log a debug message.
Log logger = LogFactory.getLog(getClass());
if (logger.isTraceEnabled()) {
logger.trace("Non-matching event type for listener: " + listener, ex);
}
}
else {
throw ex;
}
}
}
可以看到绍撞,最終調(diào)用的是 doInvokeListener
方法正勒,在該方法中執(zhí)行了 ApplicationListener
的 onApplicationEvent
方法,入?yún)閺V播的事件對象傻铣。我們就拿其中一個的監(jiān)聽器來看看 onApplicationEvent
中的實(shí)現(xiàn)章贞,如 BackgroundPreinitializer
類:
public class BackgroundPreinitializer implements ApplicationListener<SpringApplicationEvent> {
...
@Override
public void onApplicationEvent(SpringApplicationEvent event) {
if (!Boolean.getBoolean(IGNORE_BACKGROUNDPREINITIALIZER_PROPERTY_NAME)
&& event instanceof ApplicationStartingEvent && preinitializationStarted.compareAndSet(false, true)) {
performPreinitialization();
}
if ((event instanceof ApplicationReadyEvent || event instanceof ApplicationFailedEvent)
&& preinitializationStarted.get()) {
try {
preinitializationComplete.await();
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
...
}
在該方法中,通過 instanceof
判斷事件的類型非洲,從而進(jìn)行相應(yīng)的操作鸭限。該監(jiān)聽器主要的操作是新建一個后臺線程去執(zhí)行那些耗時的初始化工作,包括驗(yàn)證器怪蔑、消息轉(zhuǎn)換器等里覆。LoggingApplicationListener
監(jiān)聽器則是對 Spring Boot
的日志系統(tǒng)做一些初始化的前置操作。另外兩個監(jiān)聽器在該階段無任何操作缆瓣。
至此喧枷,SpringBoot
事件機(jī)制的整體流程大概如此,我們簡要回顧一下幾個核心組件:
-
SpringApplicationRunListeners
:首先弓坞,在run
方法的執(zhí)行過程中隧甚,通過該類在SpringBoot
不同的階段調(diào)用不同的階段方法,如在剛啟動階段調(diào)用的starting
方法渡冻。 -
SpringApplicationRunListener
:而SpringApplicationRunListeners
屬于組合模式的實(shí)現(xiàn)戚扳,它里面關(guān)聯(lián)了SpringApplicationRunListener
實(shí)現(xiàn)類集合,當(dāng)外部調(diào)用階段方法時族吻,會迭代執(zhí)行該集合中的階段方法帽借。實(shí)現(xiàn)類集合是spring.factories
文件中定義好的類。這里是一個擴(kuò)展點(diǎn)超歌,詳細(xì)的后面述說砍艾。 -
EventPublishingRunListener
:該類是Spring Boot
內(nèi)置的SpringApplicationRunListener
唯一實(shí)現(xiàn)類,所以巍举,當(dāng)外部調(diào)用各階段的方法時脆荷,真正執(zhí)行的是該類中的方法。 -
SimpleApplicationEventMulticaster
:在階段方法中,會通過Spring
的SimpleApplicationEventMulticaster
事件廣播器蜓谋,廣播各個階段對應(yīng)的事件梦皮,如這里的starting
方法廣播的事件是ApplicationStartingEvent
。 -
ApplicationListener
:最后ApplicationListener
的實(shí)現(xiàn)類也就是Spring Boot
監(jiān)聽器會監(jiān)聽到廣播的事件桃焕,根據(jù)不同的事件剑肯,進(jìn)行相應(yīng)的操作。這里的Spring Boot
監(jiān)聽器是也是在spring.factories
中定義好的覆旭,這里我們也可自行擴(kuò)展退子。
到這里 Spring Boot
事件監(jiān)聽機(jī)制差不多就結(jié)束了,值得注意的是 Spring Boot
監(jiān)聽器實(shí)現(xiàn)的是 Spring
的 ApplicationListener
類型将,事件類最終繼承的也是 Spring
的 ApplicationEvent
類,所以荐虐,Spring Boot
的事件和監(jiān)聽機(jī)制都基于 Spring
而實(shí)現(xiàn)的七兜。
2.2 ApplicationArguments 加載啟動參數(shù)
????????當(dāng)執(zhí)行完 listeners.starting
方法后,接著進(jìn)入構(gòu)造 ApplicationArguments
階段:
public class SpringApplication {
...
public ConfigurableApplicationContext run(String... args) {
...
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
...
}
}
...
}
該類是用于簡化 Spring Boot
應(yīng)用啟動參數(shù)的封裝接口福扬,我們啟動項(xiàng)目時輸入的命令參數(shù)會封裝在該類中腕铸。一種是通過 IDEA 輸入的參數(shù),如下:
另一種是 springboot
jar包運(yùn)行時傳遞的參數(shù):
cmd中運(yùn)行java -jar xxx.jar name=張三 pwa=123
铛碑。
然后狠裹,可以通過 @Autowired
注入 ApplicationArguments
的方式進(jìn)行使用:
public class Test {
@Autowired
private ApplicationArguments applicationArguments;
public void getArgs() {
// 獲取 args 中的所有 non option 參數(shù)
applicationArguments.getNonOptionArgs();
// 獲取 args 中所有的 option 參數(shù)的 name
applicationArguments.getOptionNames();
// 獲取傳遞給應(yīng)用程序的原始未處理參數(shù)
applicationArguments.getSourceArgs();
// 獲取 args 中指定 name 的 option 參數(shù)的值
applicationArguments.getOptionValues("nmae");
// 判斷從參數(shù)中解析的 option 參數(shù)是否包含指定名稱的選項(xiàng)
applicationArguments.containsOption("name");
}
}
2.3 ConfigurableEnvironment 加載外部化配置
????????接著進(jìn)入構(gòu)造 ConfigurableEnvironment
的階段,該類是用來處理我們外部化配置的汽烦,如 properties
涛菠、YAML
等,提供對配置文件的基礎(chǔ)操作撇吞。當(dāng)然俗冻,它能處理的外部配置可不僅僅如此,詳細(xì)的在下篇文章討論牍颈,這里我們進(jìn)行簡要了解即可迄薄,進(jìn)入創(chuàng)建該類的 prepareEnvironment
方法:
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
這里通過 getOrCreateEnvironment
方法返回具體的 Environment
:
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
可以看到,這里通過 webApplicationType
屬性來判斷當(dāng)前應(yīng)用的類型煮岁,有 Servlet
讥蔽、 Reactive
、 非Web 3種類型画机,該屬性也是在上篇文章中 SpringApplication
準(zhǔn)備階段確定的冶伞,這里我們通常都是 Servlet
類型,返回的是 StandardServletEnvironment
實(shí)例色罚。
之后碰缔,還調(diào)用了 SpringApplicationRunListeners
的 environmentPrepared
階段方法,表示 ConfigurableEnvironment
構(gòu)建完成戳护,同時向 Spring Boot
監(jiān)聽器發(fā)布 ApplicationEnvironmentPreparedEvent
事件金抡。監(jiān)聽該事件的監(jiān)聽器有:
2.4 ConfigurableApplicationContext 創(chuàng)建 Spring 應(yīng)用上下文
????????這里通過 createApplicationContext
方法創(chuàng)建 Spring
應(yīng)用上下文瀑焦,實(shí)際上 Spring
的應(yīng)用上下文才是驅(qū)動 Spring Boot
的核心引擎:
public class SpringApplication {
...
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
+ "annotation.AnnotationConfigApplicationContext";
public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
+ "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
...
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
...
}
這里也是通過 webApplicationType
屬性來確定應(yīng)用類型從而創(chuàng)建 String
上下文,上篇文章說到該屬性值是在 Spring Boot
準(zhǔn)備階段推導(dǎo)出來的梗肝。這里我們的應(yīng)用類型是 Servlet
榛瓮,所以創(chuàng)建的是 AnnotationConfigServletWebServerApplicationContext
對象。創(chuàng)建完 Spring
應(yīng)用上下文之后巫击,執(zhí)行 prepareContext
方法進(jìn)入準(zhǔn)備上下文階段:
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
我們來看看主要做了哪些操作:
- 設(shè)置了
Spring
應(yīng)用上下文的ApplicationArguments
禀晓,上面說過是處理外部化配置的,具體類型為StandardServletEnvironment
坝锰。 -
Spring
應(yīng)用上下文后置處理粹懒,主要是覆蓋當(dāng)前Spring
應(yīng)用上下文默認(rèn)所關(guān)聯(lián)的ResourceLoader
和ClassLoader
。 - 執(zhí)行
Spring
的初始化器顷级,上篇文章說過在Spring Boot
準(zhǔn)備階段初始化了一批在spring.factories
文件中定義好的ApplicationContextInitializer
凫乖,這里就是執(zhí)行它們的initialize
方法,同時這里也是一個擴(kuò)展點(diǎn)弓颈,后面詳細(xì)討論帽芽。 - 執(zhí)行
SpringApplicationRunListeners
的contextPrepared
階段方法,表示ApplicationContext
準(zhǔn)備完成翔冀,同時向Spring Boot
監(jiān)聽器發(fā)布ApplicationContextInitializedEvent
事件 导街。 - 將
springApplicationArguments
和springBootBanner
注冊為Bean
。 - 加載
Spring
應(yīng)用上下文的配置源纤子,也是在上篇文章Spring Boot
準(zhǔn)備階段獲取的primarySources
和sources
搬瑰,primarySources
來源于SpringApplication
構(gòu)造器參數(shù),sources
則來源于自定義配置的setSources
方法计福。 - 最后執(zhí)行
SpringApplicationRunListeners
的contextLoaded
階段方法跌捆,表示ApplicationContext
完成加載但還未啟動,同時向Spring Boot
監(jiān)聽器發(fā)布ApplicationPreparedEvent
事件 象颖。
接下來就是真正啟動階段佩厚,執(zhí)行的是 refreshContext
方法:
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();
}
可以看到,底層調(diào)用的是 AbstractApplicationContext
的 refresh
方法说订,到這里 Spring
應(yīng)用正式啟動抄瓦,Spring Boot
核心特性也隨之啟動,如自動裝配陶冷。隨后執(zhí)行 SpringApplicationRunListeners
的 started
階段方法钙姊,表示 ApplicationContext
已啟動,同時向 Spring Boot
監(jiān)聽器發(fā)布 ApplicationStartedEvent
事件 埂伦。但還未啟動完成煞额,后面還有一個 callRunners
方法,一般來講,里面執(zhí)行一些我們自定義的操作膊毁。之后 Spring
應(yīng)用才算啟動完成胀莹,隨后調(diào)用 running
方法,發(fā)布 ApplicationReadyEvent
事件婚温。至此描焰,SpringApplication
運(yùn)行階段結(jié)束。
3栅螟、總結(jié)
????????最后來對 SpringApplication
運(yùn)行階段做一個總結(jié)荆秦。這個階段核心還是以啟動 Spring
應(yīng)用上下文為主,同時根據(jù)應(yīng)用類型來初始化不同的上下文對象力图,但這些對象的基類都是 Spring
的 ConfigurableApplicationContext
類步绸。且在啟動的各個階段中,使用 SpringApplicationRunListeners
進(jìn)行事件廣播吃媒,回調(diào) Spring Boot
的監(jiān)聽器靡努。同時還初始化了 ApplicationArguments
、ConfigurableEnvironment
等幾個組件晓折。下篇文章我們就來討論 Spring Boot
的外部化配置部分,來看看為什么外部的各個組件兽泄,如 Redis
漓概、Dubbo
等在 properties
文件中進(jìn)行相應(yīng)配置后,就可以正常使用病梢。
以上就是本章的內(nèi)容胃珍,如過文章中有錯誤或者需要補(bǔ)充的請及時提出,本人感激不盡蜓陌。
參考:
《Spring Boot 編程思想》
https://www.cnblogs.com/youzhibing/p/9603119.html
http://www.reibang.com/p/b86a7c8b3442
https://www.cnblogs.com/duanxz/p/11243271.html
http://www.reibang.com/p/7a674c59d76e