SpringBoot启动过程广播的事件有什么作用?

关联博文:
Spring中事件监听(通知)机制详解与实践
SpringBoot中事件广播体系梳理
SpringBoot启动过程广播的事件有什么作用?

前面我们分析了SpringBoot的启动流程,其广播了诸多事件,本文我们尝试总结一下这些事件所带来的影响。

事件梳理

在启动过程中主要广播了如下事件:

  • listeners.starting()方法广播ApplicationStartingEvent事件
  • environmentPrepared()方法广播ApplicationEnvironmentPreparedEvent事件
  • contextPrepared()方法广播ApplicationContextInitializedEvent事件
  • contextLoaded()方法广播ApplicationPreparedEvent事件
  • AbstractApplicationContext的finishRefresh()方法广播ContextRefreshedEvent事件
  • ServletWebServerApplicationContext的finishRefresh()方法startWebServer后会广播 ServletWebServerInitializedEvent 事件
  • listeners.started()方法广播ApplicationStartedEvent事件
  • listeners.running()方法广播ApplicationReadyEvent事件

如下七种事件均是SpringApplicationEvent事件体系。

  • ApplicationStartingEvent:应用启动事件
  • ApplicationEnvironmentPreparedEvent:准备环境事件
  • ApplicationContextInitializedEvent:应用初始化事件
  • ApplicationPreparedEvent:应用准备好事件
  • ApplicationStartedEvent:应用启动事件
  • ApplicationReadyEvent:应用就绪事件
  • ApplicationFailedEvent:应用失败事件

environmentPrepared方法在创建并配置环境的过程中,contextPrepared()和contextLoaded()则是在prepareContext过程中。

其中如下三个在SpringApplication的run方法主流程中:

  • listeners.starting();
  • listeners.started(context);
  • listeners.running(context);

如下四种是ApplicationContextEvent 体系:

  • ContextRefreshedEvent (应用刷新事件)
  • ContextStartedEvent (应用启动事件)
  • ContextClosedEvent (应用关闭事件)
  • ContextStoppedEvent (应用停止事件)
事件感兴趣的监听器有用的监听器
ApplicationStartingEventRestartApplicationListener
LoggingApplicationListener
BackgroundPreinitializer
DelegatingApplicationListener
LiquibaseServiceLocatorApplicationListener
RestartApplicationListener
LoggingApplicationListener
BackgroundPreinitializer
ApplicationEnvironmentPreparedEventRestartApplicationListener
ConfigFileApplicationListener
AnsiOutputApplicationListener
LoggingApplicationListener
BackgroundPreinitializer
ClasspathLoggingApplicationListener
DelegatingApplicationListener
FileEncodingApplicationListener
ConfigFileApplicationListener
AnsiOutputApplicationListener
LoggingApplicationListener
ClasspathLoggingApplicationListener
FileEncodingApplicationListener
ApplicationContextInitializedEventRestartApplicationListener
BackgroundPreinitializer
DelegatingApplicationListener
ApplicationPreparedEventRestartApplicationListener
CloudFoundryVcapEnvironmentPostProcessor
ConfigFileApplicationListener
LoggingApplicationListener
BackgroundPreinitializer
DelegatingApplicationListener
DevToolsLogFactory$Listener
RestartApplicationListener
CloudFoundryVcapEnvironmentPostProcessor
ConfigFileApplicationListener
LoggingApplicationListener
DevToolsLogFactory$Listener
ServletWebServerInitializedEventRestartApplicationListener
SpringApplicationAdminMXBeanRegistrar
DelegatingApplicationListener
ServerPortInfoApplicationContextInitializer
SpringApplicationAdminMXBeanRegistrar
ServerPortInfoApplicationContextInitializer
ApplicationStartedEventRestartApplicationListener
BackgroundPreinitializer
DelegatingApplicationListener
ApplicationReadyEventRestartApplicationListener
SpringApplicationAdminMXBeanRegistrar
BackgroundPreinitializer
DelegatingApplicationListener
ConditionEvaluationDeltaLoggingListener
RestartApplicationListener
SpringApplicationAdminMXBeanRegistrar
BackgroundPreinitializer
ConditionEvaluationDeltaLoggingListener

【1】ApplicationStartingEvent

对该事件感兴趣的监听器有如下五个:

0 = {RestartApplicationListener@1953} 
1 = {LoggingApplicationListener@1954} 
2 = {BackgroundPreinitializer@1955} 
3 = {DelegatingApplicationListener@1956} 
4 = {LiquibaseServiceLocatorApplicationListener@1957} 

① RestartApplicationListener

其对ApplicationStartingEvent的处理则是实例化Restarter。

② LoggingApplicationListener

如下所示,初始化loggingSystem ,本文这里获取的是LogbackLoggingSystem。beforeInitialize这里会对日志适配器、桥接器进行处理。

private void onApplicationStartingEvent(ApplicationStartingEvent event) {
	this.loggingSystem = LoggingSystem.get(event.getSpringApplication().getClassLoader());
	this.loggingSystem.beforeInitialize();
}

③ BackgroundPreinitializer

如果是ApplicationStartingEvent事件时,这个玩意会尝试以多线程方式进行一些"基础设置服务类"的预先配置。

具体信息可以参考博文:SpringBoot中事件广播体系梳理,这里不再赘述。

④ DelegatingApplicationListener

如下所示,当ApplicationStartingEvent事件过来时,这个监听器并无作为。

public void onApplicationEvent(ApplicationEvent event) {
	if (event instanceof ApplicationEnvironmentPreparedEvent) {
		List<ApplicationListener<ApplicationEvent>> delegates = getListeners(
				((ApplicationEnvironmentPreparedEvent) event).getEnvironment());
		if (delegates.isEmpty()) {
			return;
		}
		this.multicaster = new SimpleApplicationEventMulticaster();
		for (ApplicationListener<ApplicationEvent> listener : delegates) {
			this.multicaster.addApplicationListener(listener);
		}
	}
	if (this.multicaster != null) {
		this.multicaster.multicastEvent(event);
	}
}

其getListeners方法则是尝试从环境中获取属性context.listener.classes对应的监听器配置并见实例化。

⑤ LiquibaseServiceLocatorApplicationListener

Liquibase是一个用于跟踪、管理和应用数据库变化的开源的数据库重构工具。它将所有数据库的变化(包括结构和数据)都保存在XML文件中,便于版本控制。本文这里该监听器毫无作为。

@Override
public void onApplicationEvent(ApplicationStartingEvent event) {
	if (ClassUtils.isPresent("liquibase.servicelocator.CustomResolverServiceLocator",
			event.getSpringApplication().getClassLoader())) {
		new LiquibasePresent().replaceServiceLocator();
	}
}

【2】ApplicationEnvironmentPreparedEvent

对该事件感兴趣的监听器有如下五个:

0 = {RestartApplicationListener@2009} 
1 = {ConfigFileApplicationListener@2419} 
2 = {AnsiOutputApplicationListener@2420} 
3 = {LoggingApplicationListener@2013} 
4 = {BackgroundPreinitializer@2421} 
5 = {ClasspathLoggingApplicationListener@2422} 
6 = {DelegatingApplicationListener@2084} 
7 = {FileEncodingApplicationListener@2423} 

① RestartApplicationListener、BackgroundPreinitializer

其并没有对ApplicationEnvironmentPreparedEvent事件做处理。

② ConfigFileApplicationListener

获取配置的EnvironmentPostProcessor,添加当前实例后,遍历触发每个EnvironmentPostProcessor 的postProcessEnvironment方法。

private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
	List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
	postProcessors.add(this);
	AnnotationAwareOrderComparator.sort(postProcessors);
	for (EnvironmentPostProcessor postProcessor : postProcessors) {
		postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
	}
}

这里获取的EnvironmentPostProcessor 有如下8个:

0 = {SystemEnvironmentPropertySourceEnvironmentPostProcessor@2463} 
1 = {SpringApplicationJsonEnvironmentPostProcessor@2464} 
2 = {CloudFoundryVcapEnvironmentPostProcessor@2465} 
3 = {ConfigFileApplicationListener@2419} 
4 = {SafetyEncryptProcessor@2466} 
5 = {DevToolsHomePropertiesPostProcessor@2467} 
6 = {DevToolsPropertyDefaultsPostProcessor@2468} 
7 = {DebugAgentEnvironmentPostProcessor@2469} 

③ AnsiOutputApplicationListener

该监听器是根据spring.output.ansi.enabled参数配置AnsiOutput

@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
	ConfigurableEnvironment environment = event.getEnvironment();
	Binder.get(environment).bind("spring.output.ansi.enabled", AnsiOutput.Enabled.class)
			.ifBound(AnsiOutput::setEnabled);
	AnsiOutput.setConsoleAvailable(environment.getProperty("spring.output.ansi.console-available", Boolean.class));
}

④ LoggingApplicationListener

如下所示,判断并获取loggingSystem,然后进行日志的初始化任务,比如设置日志级别。

private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
	if (this.loggingSystem == null) {
		this.loggingSystem = LoggingSystem.get(event.getSpringApplication().getClassLoader());
	}
	initialize(event.getEnvironment(), event.getSpringApplication().getClassLoader());
}


protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) {
	new LoggingSystemProperties(environment).apply();
	this.logFile = LogFile.get(environment);
	if (this.logFile != null) {
		this.logFile.applyToSystemProperties();
	}
	this.loggerGroups = new LoggerGroups(DEFAULT_GROUP_LOGGERS);
	initializeEarlyLoggingLevel(environment);
	initializeSystem(environment, this.loggingSystem, this.logFile);
	initializeFinalLoggingLevels(environment, this.loggingSystem);
	registerShutdownHookIfNecessary(environment, this.loggingSystem);
}

⑤ ClasspathLoggingApplicationListener

其对ApplicationEnvironmentPreparedEventApplicationFailedEvent事件感兴趣,用来打印线程上下文类加载器( the thread context class loader (TCCL))的classpath,在debug 级别。

⑥ DelegatingApplicationListener

虽然这个监听器对ApplicationEnvironmentPreparedEvent感兴趣,但是需要配置了context.listener.classes才可以起作用,否则这个监听器并无作为。

如果配置了context.listener.classes,那么将会将事件(ApplicationEvent类型)广播给这些监听器。也就是说在application.yml或者在application.properties配置文件中通过context.listener.classes配置监听类,但是需要注意,这种配置无非监听ApplicationStartingEvent事件。

⑦ FileEncodingApplicationListener

如果配置了spring.mandatory-file-encoding那么将会比较encoding 是否与desired 相等,不相等则抛出异常。

如果没有配置spring.mandatory-file-encoding,直接返回。

String encoding = System.getProperty("file.encoding");
String desired = environment.getProperty("spring.mandatory-file-encoding");

【3】ApplicationContextInitializedEvent

准备应用上下文时会广播该事件,对该事件感兴趣的监听器如下:

0 = {RestartApplicationListener@2006} 
1 = {BackgroundPreinitializer@2403} 
2 = {DelegatingApplicationListener@2082} 

① RestartApplicationListener、BackgroundPreinitializer

其不会对ApplicationContextInitializedEvent事件感兴趣。

② DelegatingApplicationListener

这个监听器前面已经讲过,本文这里该监听器毫无作为。所以也就是说,这个事件广播,通常是没有作用的。

【4】ApplicationPreparedEvent

也就是说上下文已经准备好了,对该事件感兴趣的监听器如下:

0 = {RestartApplicationListener@2006} 
1 = {CloudFoundryVcapEnvironmentPostProcessor@3730} 
2 = {ConfigFileApplicationListener@2401} 
3 = {LoggingApplicationListener@2014} 
4 = {BackgroundPreinitializer@2403} 
5 = {DelegatingApplicationListener@2082} 
6 = {DevToolsLogFactory$Listener@3731} 

① RestartApplicationListener

该监听器会触发Restarter.getInstance().prepare(event.getApplicationContext());,最终会为当前应用上下文设置ResourceLoader(ClassLoaderFilesResourcePatternResolver),并将当前应用上下文添加到Restarter实例的List<ConfigurableApplicationContext> rootContexts中。

② CloudFoundryVcapEnvironmentPostProcessor

这个监听器的响应事件方法如下所示,只是转换了logger的所属。该监听器侧重于作为一个EnvironmentPostProcessor使用,在ConfigFileApplicationListener监器的onApplicationEnvironmentPreparedEvent方法中会触发CloudFoundryVcapEnvironmentPostProcessorpostProcessEnvironment方法。

@Override
public void onApplicationEvent(ApplicationPreparedEvent event) {
	logger.switchTo(CloudFoundryVcapEnvironmentPostProcessor.class);
}

③ ConfigFileApplicationListener

如下所示,当ApplicationPreparedEvent事件广播时,该监听器会将PropertySourceOrderingPostProcessor添加到应用上下文的List<BeanFactoryPostProcessor> beanFactoryPostProcessors集合中。

private void onApplicationPreparedEvent(ApplicationEvent event) {
	this.logger.switchTo(ConfigFileApplicationListener.class);
	addPostProcessors(((ApplicationPreparedEvent) event).getApplicationContext());
}

protected void addPostProcessors(ConfigurableApplicationContext context) {
	context.addBeanFactoryPostProcessor(new PropertySourceOrderingPostProcessor(context));
}

④ LoggingApplicationListener

LoggingApplicationListener如下所示,这里会注册日志相关的一些bean。

private void onApplicationPreparedEvent(ApplicationPreparedEvent event) {
	ConfigurableListableBeanFactory beanFactory = event.getApplicationContext().getBeanFactory();
	// springBootLoggingSystem
	if (!beanFactory.containsBean(LOGGING_SYSTEM_BEAN_NAME)) {
		beanFactory.registerSingleton(LOGGING_SYSTEM_BEAN_NAME, this.loggingSystem);
	}
	// springBootLogFile
	if (this.logFile != null && !beanFactory.containsBean(LOG_FILE_BEAN_NAME)) {
	// this.logFile指向了我们的日志文件如./recommend_logs
		beanFactory.registerSingleton(LOG_FILE_BEAN_NAME, this.logFile);
	}
	// springBootLoggerGroups
	if (this.loggerGroups != null && !beanFactory.containsBean(LOGGER_GROUPS_BEAN_NAME)) {
		beanFactory.registerSingleton(LOGGER_GROUPS_BEAN_NAME, this.loggerGroups);
	}
}

⑤ DevToolsLogFactory$Listener

从延迟日志记录切换到立即日志记录到指定目标。

BackgroundPreinitializerDelegatingApplicationListener(本文)其对ApplicationPreparedEvent事件并无作用。

【5】ContextRefreshedEvent

该事件在finishRefresh方法会广播出来,对其感兴趣的监听器如下所示:

0 = {RestartApplicationListener@2006} 
1 = {DelegatingApplicationListener@2082} 
2 = {LocalDevToolsAutoConfiguration$LiveReloadServerEventListener@9352} 
3 = {ConditionEvaluationReportLoggingListener$ConditionEvaluationReportListener@9353} 
4 = {ClearCachesApplicationListener@9354} 
5 = {SharedMetadataReaderFactoryContextInitializer$SharedMetadataReaderFactoryBean@9355} 
6 = {PluginRegistryFactoryBean@9356} 
7 = {PluginRegistryFactoryBean@9357} 
8 = {PluginRegistryFactoryBean@9358} 
9 = {PluginRegistryFactoryBean@9359} 
10 = {PluginRegistryFactoryBean@9360} 
11 = {PluginRegistryFactoryBean@9361} 
12 = {PluginRegistryFactoryBean@9362} 
13 = {PluginRegistryFactoryBean@9363} 
14 = {PluginRegistryFactoryBean@9364} 
15 = {PluginRegistryFactoryBean@9365} 
16 = {PluginRegistryFactoryBean@9366} 
17 = {PluginRegistryFactoryBean@9367} 
18 = {PluginRegistryFactoryBean@9368} 
19 = {PluginRegistryFactoryBean@9369} 
20 = {PluginRegistryFactoryBean@9370} 
21 = {PluginRegistryFactoryBean@9371} 
22 = {PluginRegistryFactoryBean@9372} 
23 = {ResourceUrlProvider@9373} 
24 = {PluginRegistryFactoryBean@9374} 
25 = {DelegatingApplicationListener@7876} 

可以看到这里有很多PluginRegistryFactoryBean,实例如下所示,这里我们暂不分析。
在这里插入图片描述

【6】ServletWebServerInitializedEvent

finishRefresh()方法startWebServer后会广播 ServletWebServerInitializedEvent 事件。对该事件感兴趣的监听器如下:

0 = {RestartApplicationListener@2006} 
1 = {SpringApplicationAdminMXBeanRegistrar@9498} 
2 = {DelegatingApplicationListener@2082} 
3 = {ServerPortInfoApplicationContextInitializer@9499} 
4 = {DelegatingApplicationListener@7876} 

RestartApplicationListener、DelegatingApplicationListener本文这里对该事件毫无作用。

① SpringApplicationAdminMXBeanRegistrar

设置SpringApplicationAdminMXBeanRegistrar.embeddedWebApplication为true。

② ServerPortInfoApplicationContextInitializer

如下所示,为环境设置属性-值。默认情况属性是local.server.port

@Override
public void onApplicationEvent(WebServerInitializedEvent event) {
	String propertyName = "local." + getName(event.getApplicationContext()) + ".port";
	setPortProperty(event.getApplicationContext(), propertyName, event.getWebServer().getPort());
}

【7】ApplicationStartedEvent

对该事件感兴趣的监听器如下所示:

0 = {RestartApplicationListener@2006} 
1 = {BackgroundPreinitializer@2403} 
2 = {DelegatingApplicationListener@2082} 
3 = {DelegatingApplicationListener@7876} 

RestartApplicationListenerDelegatingApplicationListenerBackgroundPreinitializer在本文环境下对该事件毫无作用。

【8】ApplicationReadyEvent

对该事感兴趣的监听器如下所示:

0 = {RestartApplicationListener@2006} 
1 = {SpringApplicationAdminMXBeanRegistrar@9635} 
2 = {BackgroundPreinitializer@2403} 
3 = {DelegatingApplicationListener@2082} 
4 = {DelegatingApplicationListener@7876} 
5 = {ConditionEvaluationDeltaLoggingListener@9636} 

① RestartApplicationListener

触发Restarter.getInstance().finish();,当源日志延迟时,从源日志重播到目标日志并设置finished标志位true。

this.logger = DeferredLog.replay(this.logger, LogFactory.getLog(getClass()));
this.finished = true;

② SpringApplicationAdminMXBeanRegistrar

对ApplicationReadyEvent事件的响应仅仅是设置了ready标志为true。

this.ready = true;

③ BackgroundPreinitializer

如下所示,当ApplicationStartingEvent事件广播时,默认情况下BackgroundPreinitializer会以后台多线程方式实例化一些"基础设置服务类"。当ApplicationReadyEvent或者ApplicationFailedEvent事件广播时,如果这里preinitializationStarted为true,那么就除非闭锁preinitializationComplete的等待方法,直到前面提到的多线程执行完。其实通常来讲,这里不会等待,只是一种保障措施。毕竟ApplicationStartingEvent离ApplicationReadyEvent蛮远的。

在这里插入图片描述
④ ConditionEvaluationDeltaLoggingListener

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

流烟默

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

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

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

打赏作者

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

抵扣说明:

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

余额充值