【细读Spring Boot源码】监听器合集-持续更新中

前言

监听器汇总

归属监听器名称作用
cloudBootstrapApplicationListener
cloudLoggingSystemShutdownListener
cloudRestartListener
cloudLoggingSystemShutdownListener
springbootEnvironmentPostProcessorApplicationListener用于触发在spring.factories文件中注册的EnvironmentPostProcessors
BackgroundPreinitializer在后台线程触发一些耗时的早期的初始化,设置 IGNORE_BACKGROUNDPREINITIALIZER_PROPERTY_NAME为true来关闭,让类初始化在前台进行
org.springframework.boot.context.configAnsiOutputApplicationListener根据属性spring.output.ansi.enabled的值配置AnsiOutput
org.springframework.boot.context.loggingLoggingApplicationListener它将用于引导日志系统,否则将使用默认配置。

监听器详情

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:字符初始化
    在这里插入图片描述
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

园长的牧歌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值