前言
监听器汇总
归属 | 监听器名称 | 作用 |
---|---|---|
cloud | BootstrapApplicationListener | |
cloud | LoggingSystemShutdownListener | |
cloud | RestartListener | |
cloud | LoggingSystemShutdownListener | |
springboot | EnvironmentPostProcessorApplicationListener | 用于触发在spring.factories文件中注册的EnvironmentPostProcessors |
BackgroundPreinitializer | 在后台线程触发一些耗时的早期的初始化,设置 IGNORE_BACKGROUNDPREINITIALIZER_PROPERTY_NAME 为true来关闭,让类初始化在前台进行 | |
org.springframework.boot.context.config | AnsiOutputApplicationListener | 根据属性spring.output.ansi.enabled的值配置AnsiOutput |
org.springframework.boot.context.logging | LoggingApplicationListener | 它将用于引导日志系统,否则将使用默认配置。 |
监听器详情
BootstrapApplicationListener
重新创建了一个SpringApplication上下文,为了添加BootstrapImportSelectorConfiguration配置类,里面会注册所有BootstrapConfiguration类型的配置类。然后进行上下文的run,ConfigFileApplicationListener会去加载bootstrap的配置文件,整合初始化器到新上下文,详细如下分析。
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment environment = event.getEnvironment();
// 判断是否开启
if (!bootstrapEnabled(environment) && !useLegacyProcessing(environment)) {
// 通过 spring.cloud.bootstrap.enabled 配置属性 true或false
// 通过 spring.config.use-legacy-processing 配置属性 true或false
return;
}
// 不要在引导程序上下文中侦听事件。保证不会重复执行,如果有bootstrap属性名了就返回
if (environment.getPropertySources().contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
return;
}
ConfigurableApplicationContext context = null;
// 拿到 spring.cloud.bootstrap.name 配置属性,配置的名称。没有使用默认的bootstrap
String configName = environment.resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}");
// 从父上下文初始化器里获取,一般获取不到
for (ApplicationContextInitializer<?> initializer : event.getSpringApplication().getInitializers()) {
if (initializer instanceof ParentContextApplicationContextInitializer) {
context = findBootstrapContext((ParentContextApplicationContextInitializer) initializer, configName);
}
}
if (context == null) {
// 要重新创建一个上下文,为的就是来加载一些配置文件
context = bootstrapServiceContext(environment, event.getSpringApplication(), configName);
event.getSpringApplication().addListeners(new CloseContextOnFailureApplicationListener(context));
}
apply(context, event.getSpringApplication(), environment);
}
bootstrapServiceContext创建新上下文一
- 构建一个新的环境bootstrapEnvironment 。
- 给名为bootstrap的环境添加
spring.config.name
属性、spring.main.web-application-type
属性 - 如果监听器所在上下文环境存在spring.cloud.bootstrap.location就给名为bootstrap的环境添加
spring.config.location
属性 - 如果监听器所在上下文环境存在spring.cloud.bootstrap.additional-location就给名为bootstrap的环境添加
pring.config.additional-location
属性 - 过滤
StubPropertySource
类型的环境,把其余环境添加进新环境
// 构建一个新的环境bootstrapEnvironment
ConfigurableEnvironment bootstrapEnvironment = new AbstractEnvironment() {};
MutablePropertySources bootstrapProperties = bootstrapEnvironment.getPropertySources();
String configLocation = environment.resolvePlaceholders("${spring.cloud.bootstrap.location:}");
String configAdditionalLocation = environment
.resolvePlaceholders("${spring.cloud.bootstrap.additional-location:}");
// 添加属性
Map<String, Object> bootstrapMap = new HashMap<>();
bootstrapMap.put("spring.config.name", configName);
// if an app (or test) uses spring.main.web-application-type=reactive, bootstrap
// will fail
// force the environment to use none, because if though it is set below in the
// builder
// the environment overrides it
bootstrapMap.put("spring.main.web-application-type", "none");
if (StringUtils.hasText(configLocation)) {
bootstrapMap.put("spring.config.location", configLocation);
}
if (StringUtils.hasText(configAdditionalLocation)) {
bootstrapMap.put("spring.config.additional-location", configAdditionalLocation);
}
bootstrapProperties.addFirst(new MapPropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME, bootstrapMap));
//过滤 StubPropertySource 类型的环境,把其余环境添加进新环境(老的放进新的里)
for (PropertySource<?> source : environment.getPropertySources()) {
if (source instanceof StubPropertySource) {
continue;
}
bootstrapProperties.addLast(source);
}
bootstrapServiceContext创建新上下文二
使用建造者模式构造一个SpringApplication
的builder。并向程序添加BootstrapImportSelectorConfiguration源
// TODO: is it possible or sensible to share a ResourceLoader?
// 设置活动的配置文件
SpringApplicationBuilder builder = new SpringApplicationBuilder().profiles(environment.getActiveProfiles())
// 设置这个spring应用不打印横幅。设置环境为上面新建环境
.bannerMode(Mode.OFF).environment(bootstrapEnvironment)
// Don't use the default properties in this builder
// 不用关机钩子。不记录启动信息。无web启动
.registerShutdownHook(false).logStartupInfo(false).web(WebApplicationType.NONE);
// 获取这个新的SpringApplication
final SpringApplication builderApplication = builder.application();
if (builderApplication.getMainApplicationClass() == null) {
// gh_425:
// SpringApplication cannot deduce the MainApplicationClass here
// if it is booted from SpringBootServletInitializer due to the
// absense of the "main" method in stackTraces.
// But luckily this method's second parameter "application" here
// carries the real MainApplicationClass which has been explicitly
// set by SpringBootServletInitializer itself already.
// 翻译上面的话:如果由于stackTraces中缺少“main”方法而从SpringBootServletInitializer启动,
// 则SpringApplication无法在此处推导MainApplicationClass。但幸运的是,
// 这个方法的第二个参数“application”携带了真正的MainApplicationClass,
// 它已经由SpringBootServletInitializer自己显式设置了。
builder.main(application.getMainApplicationClass());
}
if (environment.getPropertySources().contains("refreshArgs")) {
// If we are doing a context refresh, really we only want to refresh the
// Environment, and there are some toxic listeners (like the
// LoggingApplicationListener) that affect global static state, so we need a
// way to switch those off.
// 关闭一些监听器
builderApplication.setListeners(filterListeners(builderApplication.getListeners()));
}
// 向该应用程序添加更多源(配置类和组件):添加 BootstrapImportSelectorConfiguration
builder.sources(BootstrapImportSelectorConfiguration.class);
bootstrapServiceContext创建新上下文三
最终还是调用SpringApplication的run,但是里面就简单的做了一件事,注册我们的BootstrapImportSelectorConfiguration配置文件
final ConfigurableApplicationContext context = builder.run();
// gh-214 using spring.application.name=bootstrap to set the context id via
// `ContextIdApplicationContextInitializer` prevents apps from getting the actual
// spring.application.name
// during the bootstrap phase.
// 使用spring.application.name=bootstrap通过`ContextIdApplicationContextInitializer`
// 设置上下文id会阻止应用程序在引导阶段获得实际的spring.application.name
context.setId("bootstrap");
// Make the bootstrap context a parent of the app context
// 使引导程序上下文成为应用程序上下文的父上下文
addAncestorInitializer(application, context);
// It only has properties in it now that we don't want in the parent so remove
// it (and it will be added back later)
// 现在它只包含属性,因为我们不想在父级中使用它,所以将其删除(稍后会重新添加)。移除bootstrap属性
bootstrapProperties.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME);
// 把nocas加载的配置文件merge到应用程序环境里
mergeDefaultProperties(environment.getPropertySources(), bootstrapProperties);
return context;
LoggingSystemShutdownListener
Cleans up the logging system immediately after the bootstrap context is created on startup. Logging will go dark until the ConfigFileApplicationListener fires, but this is the price we pay for that listener being able to adjust the log levels according to what it finds in its own configuration.
在启动时创建bootstrap上下文后立即清理日志系统。在ConfigFileApplicationListener启动之前,日志会一直处于黑暗状态,但这是我们为监听器能够根据它在自己的配置中发现的内容来调整日志级别而付出的代价。
EnvironmentPostProcessorApplicationListener
总分为3个事件ApplicationEnvironmentPreparedEvent、ApplicationPreparedEvent、ApplicationFailedEvent
第一阶段ApplicationEnvironmentPreparedEvent
关于应用程序环境准备事件
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
// 获取环境对象
ConfigurableEnvironment environment = event.getEnvironment();
// 获取Spring应用容器
SpringApplication application = event.getSpringApplication();
//
for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(),
event.getBootstrapContext())) {
postProcessor.postProcessEnvironment(environment, application);
}
}
第二阶段ApplicationPreparedEvent
第三阶段ApplicationFailedEvent
LoggingApplicationListener
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationStartingEvent) {
onApplicationStartingEvent((ApplicationStartingEvent) event);
}
else if (event instanceof ApplicationEnvironmentPreparedEvent) {
// 应用程序环境准备事件
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
}
else if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent((ApplicationPreparedEvent) event);
}
else if (event instanceof ContextClosedEvent) {
onContextClosedEvent((ContextClosedEvent) event);
}
else if (event instanceof ApplicationFailedEvent) {
onApplicationFailedEvent();
}
}
AnsiOutputApplicationListener
BackgroundPreinitializer
后台初始化有5项:
- ConversionServiceInitializer:Spring的ConversionService的早期初始值设定项
- ValidationInitializer:javax.validation的早期初始值设定项
- MessageConverterInitializer:Spring MessageConverters的早期初始值设定项。
- JacksonInitializer:Jackson的早期初始化程序
- CharsetInitializer:字符初始化