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
單獨啟動線程去完成消息轉換器、驗證器等的初始化
