关联博文:
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 (应用停止事件)

事件

感兴趣的监听器

有用的监听器

ApplicationStartingEvent

RestartApplicationListener

LoggingApplicationListener

BackgroundPreinitializer

DelegatingApplicationListener

LiquibaseServiceLocatorApplicationListener

RestartApplicationListener

LoggingApplicationListener

BackgroundPreinitializer

ApplicationEnvironmentPreparedEvent

RestartApplicationListener

ConfigFileApplicationListener

AnsiOutputApplicationListener

LoggingApplicationListener

BackgroundPreinitializer

ClasspathLoggingApplicationListener

DelegatingApplicationListener

FileEncodingApplicationListener

ConfigFileApplicationListener

AnsiOutputApplicationListener

LoggingApplicationListener

ClasspathLoggingApplicationListener

FileEncodingApplicationListener

ApplicationContextInitializedEvent

RestartApplicationListener

BackgroundPreinitializer

DelegatingApplicationListener

ApplicationPreparedEvent

RestartApplicationListener

CloudFoundryVcapEnvironmentPostProcessor

ConfigFileApplicationListener

LoggingApplicationListener

BackgroundPreinitializer

DelegatingApplicationListener

DevToolsLogFactory$Listener

RestartApplicationListener

CloudFoundryVcapEnvironmentPostProcessor

ConfigFileApplicationListener

LoggingApplicationListener

DevToolsLogFactory$Listener

ServletWebServerInitializedEvent

RestartApplicationListener

SpringApplicationAdminMXBeanRegistrar

DelegatingApplicationListener

ServerPortInfoApplicationContextInitializer

SpringApplicationAdminMXBeanRegistrar

ServerPortInfoApplicationContextInitializer

ApplicationStartedEvent

RestartApplicationListener

BackgroundPreinitializer

DelegatingApplicationListener

ApplicationReadyEvent

RestartApplicationListener

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,实例如下所示,这里我们暂不分析。

java广播功能 spring 广播_事件广播的作用

【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蛮远的。

java广播功能 spring 广播_监听器_02


④ ConditionEvaluationDeltaLoggingListener