ApplicationListener接口只有一个onApplicationEvent方法,用来处理applicationEvent(观察者模式)
在分析springboot中的事件监听加载与运行之前,先简单介绍下Spring事件体系,方便后面更好的理解springboot的事件监听。
一、Spring事件体系,主要由事件ApplicationEvent、监听器ApplicationListener、事件源ApplicationContext组成。
1.ApplicationEvent
public abstract class ApplicationEvent extends EventObject {
public ApplicationEvent(Object source) {
super(source);
this.timestamp = System.currentTimeMillis();
}
}
继承JDK的EventObject,其中source表示事件源;ApplicationEvent的实现类ApplicationContextEvent表示容器事件,对应的事件源是ApplicationContext(Spring容器)。
2.ApplicationListener
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
void onApplicationEvent(E event);
}
继承JDK的EventListener(JDK注释所有的监听都应该继承EventListener),只有一个onApplication方法,该方法接收一个ApplicationEvent参数,根据不同的事件类型做不同处理。当事件触发时监听器会收到事件消息,并根据消息类型进行对应的处理,如果有多个消息监听器,会按照指定的顺序依次收到消息。
3.ApplicationContext
Spring容器,也是Spring体系中的事件源。该类继承了ApplicationEventPublisher接口,publishEvent(ApplicationEvent event)方法,当事件触发时,通过调用该方法进行事件发布,所有的ApplicationListener就是收到事件消息。
ApplicationContext的抽象实现类AbstractApplicationContext实现了publishEvent方法。
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext { protected void publishEvent(Object event, @Nullable ResolvableType eventType) { ... // Multicast right now if possible - or lazily once the multicaster is initialized if (this.earlyApplicationEvents != null) { this.earlyApplicationEvents.add(applicationEvent); } else { getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType); } if (this.parent != null) { if (this.parent instanceof AbstractApplicationContext) { ((AbstractApplicationContext) this.parent).publishEvent(event, eventType); } else { this.parent.publishEvent(event); } } } }
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
事件的发布是通过ApplicationEventMulticaster进行事件广播。
4.ApplicationEventMulticaster
事件广播器,作用是将ApplicationContext发布的事件广播给监听器ApplicationListener。其中定义的主要方法有:注册监听器、移除监听器和广播事件
其抽象继承类AbstractApplicationEventMulticaster只实现了注册和移除监听器方法;SimpleApplicationEventMulticaster继承自AbstractApplicationEventMulticaster,实现了广播事件方法。
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster { @Override public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) { Executor executor = getTaskExecutor(); if (executor != null) { executor.execute(() -> invokeListener(listener, event)); } else { invokeListener(listener, event); } } } }
invokeListener(listener, event)方法,会调用listener.onApplicationEvent(event); 也就是事件广播的本质就是调用所有的监听器的onApplicationEvent方法。
二、SpringApplicationRunListeners和SpringApplicationRunListener
1.SpringApplicationRunListener接口方法对应了Springboot容器的生命周期,其作用是在Springboot容器的生命周期广播不同的事件;
其实现类EventPublishingRunListener实现了在Springboot生命周期内如何广播不同事件,内部持有一个事件广播器initialMulticaster(类型SimpleApplicationEventMulticaster),通过它广播不同的事件(调用所有的ApplicationListener的onApplicationEvent方法)。EventPublishingRunListener源码如下:
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
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);
}
}
// 启动时的初始化
@Override
public void starting() {
this.initialMulticaster.multicastEvent(
new ApplicationStartingEvent(this.application, this.args));
}
// 运行环境准备好之后
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
this.application, this.args, environment));
}
// 容器准备好之后
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
this.initialMulticaster.multicastEvent(new ApplicationContextInitializedEvent(
this.application, this.args, context));
}
// 容器加载好之后,该方法同时会将所有的监听器设置到容器的applicationListeners属性
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
for (ApplicationListener<?> listener : this.application.getListeners()) {
if (listener instanceof ApplicationContextAware) {
((ApplicationContextAware) listener).setApplicationContext(context);
}
context.addApplicationListener(listener);
}
this.initialMulticaster.multicastEvent(
new ApplicationPreparedEvent(this.application, this.args, context));
}
// 容器刷新之后事件
@Override
public void started(ConfigurableApplicationContext context) {
context.publishEvent(
new ApplicationStartedEvent(this.application, this.args, context));
}
// 启动完成事件
@Override
public void running(ConfigurableApplicationContext context) {
context.publishEvent(
new ApplicationReadyEvent(this.application, this.args, context));
}
// 加载失败
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
ApplicationFailedEvent event = new ApplicationFailedEvent(this.application,
this.args, context, exception);
if (context != null && context.isActive()) {
context.publishEvent(event);
}
else {
if (context instanceof AbstractApplicationContext) {
for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
.getApplicationListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
this.initialMulticaster.multicastEvent(event);
}
}
...
}
可以看到在started方法之前(容器刷新之前),都是直接调用this.initialMulticaster.multicastEvent(event)广播事件的;但是从started方法开始(容器刷新之后),调用的都是context.publishEvent(event);
我们看AbstractApplicationContext类的publishEvent方法,此方法内部仍然是在调用广播器广播事件,但是和initialMulticaster是不同的实例;容器刷新即AbstractApplicationContext类的refresh方法,会调用initApplicationEventMulticaster方法实例化一个SimpleApplicationEventMulticaster,让容器内部也持有一个事件广播器,然后通过自己持有的广播器广播事件。代码如下:
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
public void refresh() throws BeansException, IllegalStateException {
...
initApplicationEventMulticaster();
...
}
// 让容器内部也持有一个事件广播器
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isTraceEnabled()) {
logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
} else {
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isTraceEnabled()) {
logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
}
}
}
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
} else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent) applicationEvent).getResolvableType();
}
}
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
} else {
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
} else {
this.parent.publishEvent(event);
}
}
}
}
2.SpringApplicationRunListeners管理多个SpringApplicationRunListener,其生命周期定义和SpringApplicationRunListener一致,生命周期不同时段中,调用所有的SpringApplicationRunListener执行对应的方法。
三、ApplicationListener的加载和运行
先从Springboot2.0的启动代码开始
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(DemoApplication.class);
springApplication.run(args);
}
}
SpringApplication实例化代码如下
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 实例化ApplicationListener并排序,设置到listeners属性中
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
该代码会加载所有META-INF/spring.factories文件中的key为org.springframework.context.ApplicationListener的所有属性值(ApplicationListener实现类),通过反射实例化后进行排序,并设置到SpringApplication实例的listeners属性中。
再看SpringApplication的run方法
public ConfigurableApplicationContext run(String... args) {
...
// 实例化SpringApplicationRunListeners,其内部持有SpringApplicationRunListener集合
// springboot自带的SpringApplicationRunListener实现只有EventPublishingRunListener
SpringApplicationRunListeners listeners = getRunListeners(args);
// 启动时的初始化操作
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
// 运行环境准备事件
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 容器准备事件
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
// 刷新容器
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
// 容器刷新完成后并且CommandLineRunners还未调用前的事件
listeners.started(context);
callRunners(context, applicationArguments);
}
...
try {
// 启动完成事件
listeners.running(context);
}
...
return context;
}
1.SpringApplicationRunListeners listeners = getRunListeners(args);
该代码实例化SpringApplicationRunListeners,内部持有ApplicationRunListener集合(listeners属性),同时从META-INF/spring.factories文件中加载ApplicationRunListener的实现类EventPublishingRunListener,并设置到listeners属性中。
2.listeners.starting();
该代码是在SpringApplication的run方法开始运行时做的一些初始化工作,内部会调用EventPublishingRunListener的starting方法,通过事件广播器会调用各个事件监听器的onApplicationEvent方法处理ApplicationStartingEvent事件。
3.ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
该代码是准备运行环境操作,内部会调用EventPublishingRunListener的environmentPrepared方法,通过事件广播器广播ApplicationEnvironmentPreparedEvent事件。
4.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
该代码是容器准备操作,内部会调用EventPublishingRunListener的contextPrepared和contextLoaded方法,通过事件广播器广播ApplicationContextInitializedEvent和ApplicationPreparedEvent事件。通过调用EventPublishingRunListener.contextLoaded方法时,同时会将所有的监听器设置到context中的applicationListeners属性。
5.refreshContext(context);
该代码是容器刷新操作,内部initApplicationEventMulticaster会初始化一个事件广播器给自己持有,然后调用registerListeners方法将所有的监听器绑定到自己的广播器,此时容器本身也具有了广播事件能力。
然后会根据不同应用环境发布不同的事件
(1).普通web环境,发布ContextRefreshedEvent和ServletWebServerInitializedEvent事件。(ServletWebServerApplicationContext)
(2).响应式web环境,发布ContextRefreshedEvent和ServletWebServerInitializedEvent事件。(ReactiveWebServerInitializedEvent)
(3).非web环境,只发布ContextRefreshedEvent事件。(AbstractApplicationContext.finishRefresh方法)
具体代码如下:
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
...
public void refresh() throws BeansException, IllegalStateException {
...
// 使容器本身也持有事件广播器
initApplicationEventMulticaster();
// 容器刷新操作
onRefresh();
// 将监听器注册到容器持有的广播器上
registerListeners();
finishBeanFactoryInitialization(beanFactory);
// 容器刷新完成后的操作
finishRefresh();
...
}
protected void finishRefresh() {
...
// 发布容器刷新完成事件
publishEvent(new ContextRefreshedEvent(this));
...
}
...
}
// 普通web应用容器
public class ServletWebServerApplicationContext extends GenericWebApplicationContext
implements ConfigurableWebServerApplicationContext {
...
protected void finishRefresh() {
super.finishRefresh();
WebServer webServer = startWebServer();
if (webServer != null) {
publishEvent(new ServletWebServerInitializedEvent(webServer, this));
}
}
...
}
// 响应式web应用容器
public class ReactiveWebServerApplicationContext
extends GenericReactiveWebApplicationContext
implements ConfigurableWebServerApplicationContext {
...
@Override
protected void finishRefresh() {
super.finishRefresh();
WebServer webServer = startReactiveWebServer();
if (webServer != null) {
publishEvent(new ReactiveWebServerInitializedEvent(webServer, this));
}
}
...
}
6.listeners.started(context);
容器刷新完成之后,并且在所有的CommandLineRunner调用之前,容器发布ApplicationStartedEvent事件。
7.listeners.running(context);
应用启动完成,容器发布ApplicationReadyEvent事件。
8.handleRunFailure(context, ex, exceptionReporters, listeners);
应用启动过程中出现异常,会调用listeners.failed(context)方法,容器发布ApplicationFailedEvent事件。
从以上分析,可以看出在容器刷新之前,我们都是通过EventPublishingRunListener类的广播器进行事件发布的;在容器刷新以及刷新完成之后,都是通过容器本身进行事件发布的。
四、Springboot2.0内置的ApplicationListener
1.org.springframework.boot.ClearCachesApplicationListener
清除缓存,只监听ContextRefreshedEvent容器刷新完成事件,刷新容器刷新完成之前的反射工具中的缓存,以及类加载器以和父类加载中的所使用的缓存。
2.org.springframework.boot.builder.ParentContextCloserApplicationListener
父子结构容器关闭的监听,监听ContextClosedEvent容器关闭事件,如果有当前容器存在子容器并且在运行,就关闭子容器。
3.org.springframework.boot.context.FileEncodingApplicationListener
文件编码监听,监听ApplicationEnvironmentPreparedEvent运行环境准备完成事件,判断如果file.encoding不为空并且和spring.mandatory-file-encoding配置的文件编码不一致时,抛出异常。
4.org.springframework.boot.context.config.AnsiOutputApplicationListener
监听ApplicationEnvironmentPreparedEvent事件,配置是否支持ANSI,用于设置色彩输出日志。
5.org.springframework.boot.context.config.ConfigFileApplicationListener
加载application.properties或application.yml配置文件,监听ApplicationEnvironmentPreparedEvent和ApplicationPreparedEvent事件。
6.org.springframework.boot.context.config.DelegatingApplicationListener
将事件委托给通过context.listener.classes配置的自定义的监听器处理。
7.org.springframework.boot.context.logging.ClasspathLoggingApplicationListener
debug模式下日志输出classpath。
8.org.springframework.boot.context.logging.LoggingApplicationListener
配置日志系统,默认只输出在控制台;需要配置logging.path和logging.file输出日志文件。
9.org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
10.org.springframework.boot.autoconfigure.BackgroundPreinitializer
单独启动线程去完成消息转换器、验证器等的初始化