SpringBoot啟動過程之初始化器initializer和監聽器listeners
Listener類圖
SpringApplication構造的時候內部會呼叫一個private方法initialize:
public SpringApplication(Object... sources) { initialize(sources); // sources目前是一個MyApplication的class物件 } private void initialize(Object[] sources) { if (sources != null && sources.length > 0) { this.sources.addAll(Arrays.asList(sources)); // 把sources設定到SpringApplication的sources屬性中,目前只是一個MyApplication類物件 } this.webEnvironment = deduceWebEnvironment(); // 判斷是否是web程式(javax.servlet.Servlet和org.springframework.web.context.ConfigurableWebApplicationContext都必須在類載入器中存在),並設定到webEnvironment屬性中 // 從spring.factories檔案中找出key為ApplicationContextInitializer的類並例項化後設置到SpringApplication的initializers屬性中。這個過程也就是找出所有的應用程式初始化器 setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); // 從spring.factories檔案中找出key為ApplicationListener的類並例項化後設置到SpringApplication的listeners屬性中。這個過程就是找出所有的應用程式事件監聽器 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 找出main類,這裡是MyApplication類 this.mainApplicationClass = deduceMainApplicationClass(); }
ApplicationContextInitializer,應用程式初始化器,做一些初始化的工作:
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
void initialize(C applicationContext);
}
類名 |
---|
org.springframework.boot.context.config.DelegatingApplicationContextInitializer |
org.springframework.boot.context.ContextIdApplicationContextInitializer |
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer |
org.springframework.boot.context.embedded.ServerPortInfoApplicationContextInitializer |
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer |
org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer |
功能描述:
- DelegatingApplicationContextInitializer: 委派處理ApplicationContext初始化器,其需要委派處理的初始化器來自Spring環境中的context.initializer.classes屬性,該屬性可以使用逗號分隔多個初始化器。
- ContextIdApplicationContextInitializer:為ApplicationContext設定id。根據以下的配置順序來設定,spring.application.name、vcap.application.name、spring.config.name,如果環境配置中都沒有這些配置,則預設使用“application”來表示,另外還會將profiles也加入到id中去。
- ConfigurationWarningsApplicationContextInitializer:輸出警告日誌資訊。
- ServerPortInfoApplicationContextInitializer:新增一個EmbeddedServletContainerInitializedEvent事件監聽,觸發設定嵌入的WEB服務啟動埠。通過屬性local.[namespace].port來設定啟動埠,其中namespace為ApplicationContext指定的名稱空間,如果名稱空間為空,則使用local.server.port屬性來表示配置的埠。
- SharedMetadataReaderFactoryContextInitializer:和Spring Boot共享CachingMetadataReaderFactory。
- AutoConfigurationReportLoggingInitializer:新增一個通用的事件監聽Springboot自動配置的報表日誌輸出。
在上面的初始化器分析中,我們可以看到,通過一些屬性的讀取可以設定Spring Boot啟動過程中的一些引數。初始化器的功能可以在Spring Boot啟動前對ApplicationContext進行一些自定義操作。初始化器是在準備上下文階段呼叫的。
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
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
// Load the sources
Set<Object> sources = getSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[sources.size()]));
listeners.contextLoaded(context);
}
這裡的應用程式事件(ApplicationEvent)有應用程式啟動事件(ApplicationStartedEvent),失敗事件(ApplicationFailedEvent),準備事件(ApplicationPreparedEvent)等。
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
void onApplicationEvent(E event);
}
應用程式事件監聽器跟監聽事件是繫結的。比如ConfigServerBootstrapApplicationListener只跟ApplicationEnvironmentPreparedEvent事件繫結,LiquibaseServiceLocatorApplicationListener只跟ApplicationStartedEvent事件繫結,LoggingApplicationListener跟所有事件繫結等。
key為ApplicationListener的有:
- org.springframework.boot.context.config.ConfigFileApplicationListener 重要(讀取載入springboot配置檔案) 轉向環境和配置檔案的載入
- org.springframework.boot.context.config.AnsiOutputApplicationListener(引數spring.output.ansi.enabled)
在springboot環境準備完成以後執行,
如果你的終端支援ANSI,設定彩色輸出會讓日誌更具可讀性。 - org.springframework.boot.logging.LoggingApplicationListener 根據配置初始化日誌系統log
- org.springframework.boot.logging.ClasspathLoggingApplicationListener 程式啟動時,講classpath列印到debug日誌,啟動失敗時classpath列印到info日誌
- org.springframework.boot.autoconfigure.BackgroundPreinitializer
- org.springframework.boot.context.config.DelegatingApplicationListener(引數context.listener.classes)
把Listener轉發給配置的這些class處理,這樣可以支援外圍程式碼不去寫spring.factories中的org.springframework.context.ApplicationListener相關配置,保持springboot原來程式碼的穩定 - org.springframework.boot.builder.ParentContextCloserApplicationListener 容器關閉時發出通知,如果父容器關閉,那麼自容器也一起關閉
- org.springframework.boot.context.FileEncodingApplicationListener(引數spring.mandatory-file-encoding)
在springboot環境準備完成以後執行,獲取環境中的系統環境引數,檢測當前系統環境的file.encoding和spring.mandatory-file-encoding設定的值是否一樣,如果不一樣則丟擲異常
如果不配置spring.mandatory-file-encoding則不檢查 - org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener (引數liquibase.servicelocator.ServiceLocator)
如果存在,則使用springboot相關的版本進行替代
SpringApplication的執行
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
//從Spirng.factories中獲取key為SpringApplicationListeners的類(目前只有一個實現類EventPublishRunListener)並作為listenners屬性的一個listener
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
...
}
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
//將SpringApplication物件傳入,也就傳入了SpringApplication的listeners屬性
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
分析run方法之前,先看一下SpringApplication中的一些事件和監聽器概念。
首先是SpringApplicationRunListeners類和SpringApplicationRunListener類的介紹。
SpringApplicationRunListeners內部持有SpringApplicationRunListener集合和1個Log日誌類。用於SpringApplicationRunListener監聽器的批量執行。
SpringApplicationRunListener看名字也知道用於監聽SpringApplication的run方法的執行。
它定義了5個步驟:
- starting(run方法執行的時候立馬執行;對應事件的型別是ApplicationStartedEvent)
- environmentPrepared(ApplicationContext建立之前並且環境資訊準備好的時候呼叫;對應事件的型別是ApplicationEnvironmentPreparedEvent)
- contextPrepared(ApplicationContext建立好並且在source載入之前呼叫一次;沒有具體的對應事件)
- contextLoaded(ApplicationContext建立並載入之後並在refresh之前呼叫;對應事件的型別是ApplicationPreparedEvent)
- finished(run方法結束之前呼叫;對應事件的型別是ApplicationReadyEvent或ApplicationFailedEvent)
SpringApplicationRunListener目前只有一個實現類EventPublishingRunListener,它把監聽的過程封裝成了SpringApplicationEvent事件並讓內部屬性(intialMulticaster)ApplicationEventMulticaster介面的實現類SimpleApplicationEventMulticaster廣播出去,廣播出去的事件物件會被SpringApplication中的listeners屬性進行處理。
所以說SpringApplicationRunListener和ApplicationListener之間的關係是通過ApplicationEventMulticaster廣播出去的SpringApplicationEvent所聯絡起來的。
SpringApplicationRunListenners
class SpringApplicationRunListeners {
private final Log log;
private final List<SpringApplicationRunListener> listeners;
SpringApplicationRunListeners(Log log,
Collection<? extends SpringApplicationRunListener> listeners) {
this.log = log;
this.listeners = new ArrayList<SpringApplicationRunListener>(listeners);
}
public void starting() {
for (SpringApplicationRunListener listener : this.listeners) {
listener.starting();
}
}
public void environmentPrepared(ConfigurableEnvironment environment) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.environmentPrepared(environment);
}
}
public void contextPrepared(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.contextPrepared(context);
}
}
public void contextLoaded(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.contextLoaded(context);
}
}
public void finished(ConfigurableApplicationContext context, Throwable exception) {
for (SpringApplicationRunListener listener : this.listeners) {
callFinishedListener(listener, context, exception);
}
}
private void callFinishedListener(SpringApplicationRunListener listener,
ConfigurableApplicationContext context, Throwable exception) {
try {
listener.finished(context, exception);
}
catch (Throwable ex) {
if (exception == null) {
ReflectionUtils.rethrowRuntimeException(ex);
}
if (this.log.isDebugEnabled()) {
this.log.error("Error handling failed", ex);
}
else {
String message = ex.getMessage();
message = (message == null ? "no error message" : message);
this.log.warn("Error handling failed (" + message + ")");
}
}
}
}
EventPublishingRunListener
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 int getOrder() {
return 0;
}
@Override
@SuppressWarnings("deprecation")
public void starting() {
this.initialMulticaster
.multicastEvent(new ApplicationStartedEvent(this.application, this.args));
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
this.application, this.args, environment));
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
for (ApplicationListener<?> listener : this.application.getListeners()) {
if (listener instanceof ApplicationContextAware) {
((ApplicationContextAware) listener).setApplicationContext(context);
}
context.addApplicationListener(listener);
}
this.initialMulticaster.multicastEvent(
new ApplicationPreparedEvent(this.application, this.args, context));
}
@Override
public void finished(ConfigurableApplicationContext context, Throwable exception) {
SpringApplicationEvent event = getFinishedEvent(context, exception);
if (context != null && context.isActive()) {
// Listeners have been registered to the application context so we should
// use it at this point if we can
context.publishEvent(event);
}
else {
// An inactive context may not have a multicaster so we use our multicaster to
// call all of the context's listeners instead
if (context instanceof AbstractApplicationContext) {
for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
.getApplicationListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
if (event instanceof ApplicationFailedEvent) {
this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
}
this.initialMulticaster.multicastEvent(event);
}
}
private SpringApplicationEvent getFinishedEvent(
ConfigurableApplicationContext context, Throwable exception) {
if (exception != null) {
return new ApplicationFailedEvent(this.application, this.args, context,
exception);
}
return new ApplicationReadyEvent(this.application, this.args, context);
}
private static class LoggingErrorHandler implements ErrorHandler {
private static Log logger = LogFactory.getLog(EventPublishingRunListener.class);
@Override
public void handleError(Throwable throwable) {
logger.warn("Error calling ApplicationEventListener", throwable);
}
}
}