上文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持有了SimpleApplicationEventMulticaster 和SpringApplication 。SimpleApplicationEventMulticaster 用来实现事件广播,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的等待方法,直到前面提到的多线程执行完。其实通常来讲,这里不会等待,只是一种保障措施。毕竟ApplicationStartingEvent离ApplicationReadyEvent蛮远的。
@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
方法进行一些实例的初始化。当然事件为ApplicationReadyEvent
或ApplicationFailedEvent
且preinitializationStarted
这个原子对象值为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进行ValidatorFactory
和Validator
创建与配置。
MessageConverterInitializer主要用来实例化AllEncompassingFormHttpMessageConverter,其构造方法中会实例化一些MessageConverter并添加到FormHttpMessageConverter.partConverters集合中
JacksonInitializer主要针对Jackson的一些配置,这里会实例化Jackson2ObjectMapperBuilder并进行配置。
CharsetInitializer则会触发StandardCharsets.UTF_8.name();,会导致StandardCharsets这个final类的加载和常量赋值。
上面几个XXXXInitializer均实现了Runnable接口。关于CountDownLatch 更多信息可以参考博文:多线程并发之CountDownLatch(闭锁)使用详解
③ ConfigFileApplicationListener
其继承树示意图如下所示,除了是一个ApplicationListener外,其还是一个EnvironmentPostProcessor。其对ApplicationEnvironmentPreparedEvent
和
ApplicationPreparedEvent
事件感兴趣。
onApplicationEnvironmentPreparedEvent
onApplicationEnvironmentPreparedEvent
方法如下所示,会通过
SpringFactoriesLoader.loadFactories
方法获取配置的EnvironmentPostProcessor
,然后添加自身后进行遍历触发每一个EnvironmentPostProcessor
的postProcessEnvironment
方法。
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
其对ApplicationEnvironmentPreparedEvent和ApplicationFailedEvent事件感兴趣,用来打印线程上下文类加载器( 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
与应用端口。