SpringBoot中事件广播体系梳理

本文深入剖析SpringBoot中的事件广播机制,包括事件监听器的实现、事件的触发流程及核心组件SimpleApplicationEventMulticaster的工作原理。同时,介绍了多种内置监听器的功能,如RestartApplicationListener、BackgroundPreinitializer等。

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

上文Spring中事件监听(通知)机制详解与实践我们研究是Spring中的事件机制原理并进行了实践,本文我们梳理一下SpringBoot中事件广播的几个分支。

【1】持有分支

事件广播是使用SimpleApplicationEventMulticaster这个应用事件广播器进行处理的。所以我们只需要看这个实例被哪些类持有。

① AbstractApplicationContext

也就是应用的"高级"容器,内部持有了基础的IOC容器DefaultListableBeanFactory。如果是我们自定义事件,那么也是通过这个容器持有的事件广播器进行广播的。

@Nullable
private ApplicationEventMulticaster applicationEventMulticaster;

如下所示,在refresh过程中会为应用上下文实例化一个事件广播器。
在这里插入图片描述

② EventPublishingRunListener

其实现了SpringApplicationRunListener接口,用来发布SpringApplicationEvent事件(通过其持有的事件广播器)。如下七种事件均是SpringApplicationEvent事件体系。

  • starting()方法广播ApplicationStartingEvent事件
  • environmentPrepared()方法广播ApplicationEnvironmentPreparedEvent事件
  • contextPrepared()方法广播ApplicationContextInitializedEvent事件
  • contextLoaded()方法广播ApplicationPreparedEvent事件
  • started()方法广播ApplicationStartedEvent事件
  • running()方法广播ApplicationReadyEvent事件
  • failed()方法广播ApplicationFailedEvent事件

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

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

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

如下构造方法所示,EventPublishingRunListener持有了SimpleApplicationEventMulticasterSpringApplicationSimpleApplicationEventMulticaster 用来实现事件广播,SpringApplication 则是将其监听器添加到事件广播的成员中。

private final SpringApplication application;

private final String[] args;

private final SimpleApplicationEventMulticaster initialMulticaster;

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);
	}
}

③ DelegatingApplicationListener

DelegatingApplicationListener的核心成员和构造方法如下所示,维护了一个事件广播器,对ApplicationEnvironmentPreparedEvent事件感兴趣。

private SimpleApplicationEventMulticaster multicaster;

@Override
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);
	}
}

【2】ApplicationListener

由应用程序事件侦听器实现的接口,是一个功能性接口,同时是基于观察器设计模式的标准{java.util.EventListener}接口。

从Spring 3.0开始,ApplicationListener可以通用地声明它感兴趣的事件类型。当向Spring ApplicationContext 注册时,事件将被相应地过滤,侦听器仅当匹配事件对象时被调用。

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
	 // 处理applicationEvent
	void onApplicationEvent(E event);

}

我们简单梳理一些常见的内置监听器。

① RestartApplicationListener

如下所示,其贯穿整个应用的生命周期并通过一个Restarter实例来完成不同的动作。

@Override
public void onApplicationEvent(ApplicationEvent event) {
	if (event instanceof ApplicationStartingEvent) {
		onApplicationStartingEvent((ApplicationStartingEvent) event);
	}
	if (event instanceof ApplicationPreparedEvent) {
		onApplicationPreparedEvent((ApplicationPreparedEvent) event);
	}
	if (event instanceof ApplicationReadyEvent || event instanceof ApplicationFailedEvent) {
		Restarter.getInstance().finish();
	}
	if (event instanceof ApplicationFailedEvent) {
		onApplicationFailedEvent((ApplicationFailedEvent) event);
	}
}

ApplicationStartingEvent事件触发onApplicationStartingEvent方法,用来完成Restarter的实例化。

ApplicationPreparedEvent事件触发onApplicationPreparedEvent方法,触发Restarter实例的prepare方法,最终为applicationContext设置ClassLoaderFilesResourcePatternResolver这样一个ResourceLoader,并为Restarter实例维护的rootContexts添加当前applicationContext。

ApplicationReadyEvent事件会触发Restarter实例的finish方法,修改Restarter实例的finished标识为true。

ApplicationFailedEvent事件除了做ApplicationReadyEvent事件一样的操作完,还会触发onApplicationFailedEvent方法,进而触发Restarter实例的remove方法,从Restarter实例维护的rootContexts中移除当前applicationContext。

② BackgroundPreinitializer

当listener.starting方法触发的时候,广播ApplicationStartingEvent事件就会触发到这个监听器的执行。

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

@Order(LoggingApplicationListener.DEFAULT_ORDER + 1)
public class BackgroundPreinitializer implements ApplicationListener<SpringApplicationEvent> {

//指示Spring Boot如何运行预初始化的系统属性。
//当属性设置为{true}时,不会发生预初始化,并且每个项都会根据需要在前台初始化。
//当属性为{@code false}(默认值)时,预初始化在后台的单独线程中运行。
	public static final String IGNORE_BACKGROUNDPREINITIALIZER_PROPERTY_NAME = "spring.backgroundpreinitializer.ignore";

	private static final AtomicBoolean preinitializationStarted = new AtomicBoolean(false);
	// 闭锁,用来控制线程的执行
	private static final CountDownLatch preinitializationComplete = new CountDownLatch(1);

	@Override
	public void onApplicationEvent(SpringApplicationEvent event) {
		if (!Boolean.getBoolean(IGNORE_BACKGROUNDPREINITIALIZER_PROPERTY_NAME)
				&& event instanceof ApplicationStartingEvent && multipleProcessors()
				&& preinitializationStarted.compareAndSet(false, true)) {
			performPreinitialization();
		}
		if ((event instanceof ApplicationReadyEvent || event instanceof ApplicationFailedEvent)
				&& preinitializationStarted.get()) {
			try {
			// 等待,直到闭锁持有计数为0或者发生了线程中断
				preinitializationComplete.await();
			}
			catch (InterruptedException ex) {
				Thread.currentThread().interrupt();
			}
		}
	}
	private boolean multipleProcessors() {
		return Runtime.getRuntime().availableProcessors() > 1;
	}

如上所示,当进行了条件判断后会触发performPreinitialization方法进行一些实例的初始化。当然事件为ApplicationReadyEventApplicationFailedEventpreinitializationStarted这个原子对象值为true时,会触发闭锁的await()方法,直到performPreinitialization方法中的线程任务执行完毕。

而performPreinitialization()方法如下所示,其首先触发了五个线程,然后preinitializationComplete.countDown()释放闭锁资源。

private void performPreinitialization() {
	try {
		Thread thread = new Thread(new Runnable() {
			@Override
			public void run() {
				runSafely(new ConversionServiceInitializer());
				runSafely(new ValidationInitializer());
				runSafely(new MessageConverterInitializer());
				runSafely(new JacksonInitializer());
				runSafely(new CharsetInitializer());
				preinitializationComplete.countDown();
			}
			public void runSafely(Runnable runnable) {
				try {
					runnable.run();
				}
				catch (Throwable ex) {
					// Ignore
				}
			}
		}, "background-preinit");
		thread.start();
	}
	catch (Exception ex) {
		preinitializationComplete.countDown();
	}
}

ConversionServiceInitializer主要用来实例化DefaultFormattingConversionService,其实例化时会实例化并注册一些DefaultConverters和DefaultFormatters。

在这里插入图片描述

ValidationInitializer主要用来针对javax.validation进行ValidatorFactoryValidator创建与配置。

MessageConverterInitializer主要用来实例化AllEncompassingFormHttpMessageConverter,其构造方法中会实例化一些MessageConverter并添加到FormHttpMessageConverter.partConverters集合中

JacksonInitializer主要针对Jackson的一些配置,这里会实例化Jackson2ObjectMapperBuilder并进行配置。

CharsetInitializer则会触发StandardCharsets.UTF_8.name();,会导致StandardCharsets这个final类的加载和常量赋值。

上面几个XXXXInitializer均实现了Runnable接口。关于CountDownLatch 更多信息可以参考博文:多线程并发之CountDownLatch(闭锁)使用详解

③ ConfigFileApplicationListener

其继承树示意图如下所示,除了是一个ApplicationListener外,其还是一个EnvironmentPostProcessor。其对ApplicationEnvironmentPreparedEventApplicationPreparedEvent事件感兴趣。
在这里插入图片描述

onApplicationEnvironmentPreparedEvent

onApplicationEnvironmentPreparedEvent方法如下所示,会通过SpringFactoriesLoader.loadFactories方法获取配置的EnvironmentPostProcessor,然后添加自身后进行遍历触发每一个EnvironmentPostProcessorpostProcessEnvironment方法。

private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
// 获取配置的EnvironmentPostProcessor
	List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
	// 添加自身
	postProcessors.add(this);
	AnnotationAwareOrderComparator.sort(postProcessors);
	// 循环遍历触发每个的postProcessEnvironment方法
	for (EnvironmentPostProcessor postProcessor : postProcessors) {
		postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
	}
}

postProcessEnvironment会做什么呢?以ConfigFileApplicationListener为例,如下所示

// 添加PropertySource
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
	addPropertySources(environment, application.getResourceLoader());
}

protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
// 添加random PropertySource
	RandomValuePropertySource.addToEnvironment(environment);
	new Loader(environment, resourceLoader).load();
}

其主要用来干嘛呢,简单来讲就是将我们的配置文件扫描形成PropertySource放到环境中。比如本文这里的application.properties:
在这里插入图片描述

onApplicationPreparedEvent

如下所示,其会向应用上下文的List<BeanFactoryPostProcessor> beanFactoryPostProcessors添加PropertySourceOrderingPostProcessor

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

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

④ ClasspathLoggingApplicationListener

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

@Override
public void onApplicationEvent(ApplicationEvent event) {
	if (logger.isDebugEnabled()) {
		if (event instanceof ApplicationEnvironmentPreparedEvent) {
			logger.debug("Application started with classpath: " + getClasspath());
		}
		else if (event instanceof ApplicationFailedEvent) {
			logger.debug("Application failed to start with classpath: " + getClasspath());
		}
	}
}

⑤ DelegatingApplicationListener

一个应用了委派策略的监听器,对ApplicationEvent事件感兴趣。当ApplicationEnvironmentPreparedEvent事件广播时,其会尝试从环境里面获取context.listener.classes配置信息,如果其配置的监听器不为空,则实例化SimpleApplicationEventMulticaster并广播事件给这些监听器。

如果没有配置context.listener.classes,那么该监听器在整个应用中毫无作为。

@Override
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);
	}
}

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

⑥ ServerPortInfoApplicationContextInitializer

ServerPortInfoApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, ApplicationListener<WebServerInitializedEvent>,也就是其不单单是个监听器,还是个应用上下文初始化器。

在prepareContext方法中会触发初始化器的初始化方法,这里如下所示会将自身作为监听器添加到应用上下文中。

@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
	applicationContext.addApplicationListener(this);
}

作为事件监听器时,其会创建server.ports这样一个MapPropertySource,并放入属性local.server.port与应用端口。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

流烟默

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

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

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

打赏作者

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

抵扣说明:

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

余额充值