Spring Boot 源码分析 (2) -- 初始化器和监听器
在SpringBootApplication分析中我们说过,ApplicationContextInitializer和ApplicationListener的获取,是从Jar包中的/META-INF/spring.factories文件中解析出来的。这个功能属于Spring Boot自动配置的部分。在Spring Boot启动过程中使用了一些通用的初始化器和监听器。还是以下面的这段代码作为示例代码。
@SpringBootApplication
public class Application {
/**
* 启动Spring Boot应用
*/
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
初始化器
下面的表格是Spring Boot启动时所加载到的初始化器。首先我们要明白ApplicationContextInitializer接口的功能定义,ApplicationContextInitializer是属于Spring的一个接口。接口定义是这样的:用于初始化ConfigurableApplicationContext类型的Spring上下文对象,在调用refresh方法之前执行。另外,ApplicationContextInitializer实现类需要排序,实现Ordered接口或使用@Order注解。
类名 |
---|
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);
}
监听器
上面讲初始化器的执行时在Spring调用refresh方法之前,那么监听器是如何执行的呢?下面是SpringBoot在启动过程中使用的监听器列表。
监听器列表:
类名 |
---|
org.springframework.boot.context.config.ConfigFileApplicationListener |
org.springframework.boot.context.config.AnsiOutputApplicationListener |
org.springframework.boot.logging.LoggingApplicationListener |
org.springframework.boot.logging.ClasspathLoggingApplicationListener |
org.springframework.boot.autoconfigure.BackgroundPreinitializer |
org.springframework.boot.context.config.DelegatingApplicationListener |
org.springframework.boot.builder.ParentContextCloserApplicationListener |
org.springframework.boot.ClearCachesApplicationListener |
org.springframework.boot.context.FileEncodingApplicationListener |
功能描述:
-
ConfigFileApplicationListener:监听配置文件,'application.properties' 属性文件就是在这里处理的。
-
LoggingApplicationListener:日志监听
-
DelegatingApplicationListener:通过环境变量中的context.listener.classes属性,来委派处理配置的监听器。
-
FileEncodingApplicationListener:监听spring.mandatoryFileEncoding配置的文件编码和系统文件编码是否一致。
Spring Boot提供的监听器怎么处理呢?在run(String[] agas)中我们可以看到监听器的入口在SpringApplicationRunListeners listeners = getRunListeners(args),该方法获取了所有监听器。我们看看这个方法。
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
这个方法从/META-INF/spring.fatories中获取了所有配置的SpringApplicationRunListener接口实例。这里是EventPublishingRunListener实例对象。将this和args参数作为构造参数传入。可以看EventPublishingRunListener构造方法得到证实。
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);
}
}
也就是说,Spring Boot通过EventPublishingRunListener来进行事件的发布。将所有的listener发布到SimpleApplicationEventMulticaster中。事件的通知通过调用multicastEvent方法来告诉监听器哪种事件应该被监听到。
下面我们继续看Spring Boot发布的事件。
Spring Boot启动事件 ApplicationStartedEvent
ApplicationStartedEvent事件在Spring Boot 1.5版本之后被ApplicationStartingEvent取代。
public void starting() {
this.initialMulticaster
.multicastEvent(new ApplicationStartedEvent(this.application, this.args));
}
Spring Boot 环境准备完成事件 ApplicationEnvironmentPreparedEvent
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
this.application, this.args, environment));
}
Spring Boot 上下文准备完成事件
这个是一个空方法。
public void contextPrepared(ConfigurableApplicationContext context) {
}
Spring Boot 上下文加载完成事件 ApplicationPreparedEvent
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));
}
Spring Boot 启动完成事件ApplicationReadyEvent和启动失败事件ApplicationFailedEvent
public void finished(ConfigurableApplicationContext context, Throwable exception) {
SpringApplicationEvent event = getFinishedEvent(context, exception);
if (context != null && context.isActive()) {
// 发布注册到上下文中可以发布的事件
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);
}
总结
本篇主要分析了SpringBoot在启动过程中的ApplicationContextInitializer和ApplicationListener。在SpringBoot中初始化器和监听器都是可以通过Jar包中的/META-INF/spring.factories文件来配置单。初始化器的调用是在容器加载之前调用的,可以对ApplicationContext在启动前做一些自定义初始化处理。监听器可以监听SpringBoot提供的事件,在SpringBoot中新增了几种事件类型,上面已经列出来了。总之,通过这两个接口,可以对SpringBoot在启动和运行中很轻松地做到自定义开发功能。