之前的文章分析了SpringBoot
如何實例化SpringApplication
對象,接下來分析run
方法中的事件監(jiān)聽機制,在此之前先了解下Java
的事件監(jiān)聽機制
1. Java的事件監(jiān)聽機制
Java自定義事件實現(xiàn):
- 自定義事件繼承-->
java.util.EventObject
類 - 自定義事件監(jiān)聽器實現(xiàn)-->
java.util.EventListener
接口
例:監(jiān)聽方法耗時
1.1 繼承EventObject
定義事件類型
package sample.simple.event;
import java.util.EventObject;
public class MethodMonitorEvent extends EventObject {
public long timestamp;
/**
* Constructs a prototypical Event.
*
* @param source The object on which the Event initially occurred.
* @throws IllegalArgumentException if source is null.
*/
public MethodMonitorEvent(Object source) {
super(source);
}
}
1.2 實現(xiàn)EventListener
接口定義監(jiān)聽器
package sample.simple.event;
import java.util.EventListener;
public class MethodMonitorEventListener implements EventListener {
public void onMethodBegin(MethodMonitorEvent event) {
// 記錄方法開始執(zhí)行時的時間
System.out.println("==記錄方法耗時開始");
event.timestamp = System.currentTimeMillis();
}
public void onMethodEnd(MethodMonitorEvent event) {
// 計算方法耗時
long duration = System.currentTimeMillis() - event.timestamp;
System.out.println("==記錄方法耗時結(jié)束");
System.out.println("==耗時:" + duration);
}
}
1.3 發(fā)布事件并測試
package sample.simple.event;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class MethodMonitorEventPublisher {
private List<MethodMonitorEventListener> listeners = new ArrayList<MethodMonitorEventListener>();
public void methodMonitor() throws InterruptedException {
MethodMonitorEvent eventObject = new MethodMonitorEvent(this);
publishEvent("begin", eventObject);
// 模擬方法執(zhí)行:休眠5秒鐘
TimeUnit.SECONDS.sleep(5);
publishEvent("end", eventObject);
}
private void publishEvent(String status, MethodMonitorEvent event) {
List<MethodMonitorEventListener> copyListeners = new ArrayList<MethodMonitorEventListener>(listeners);
for (MethodMonitorEventListener listener : copyListeners) {
if ("begin".equals(status)) {
listener.onMethodBegin(event);
} else {
listener.onMethodEnd(event);
}
}
}
public void addEventListener(MethodMonitorEventListener listener) {
listeners.add(listener);
}
public static void main(String[] args) throws InterruptedException {
MethodMonitorEventPublisher publisher = new MethodMonitorEventPublisher();
publisher.addEventListener(new MethodMonitorEventListener());
publisher.methodMonitor();
}
}
運行main
函數(shù)
此例,了解了
Java
的事件監(jiān)聽機制,并簡單的實現(xiàn)了一個觀察者模式,有助于接下來對SpringBoot
事件監(jiān)聽機制的了解
2. SpringBoot中的事件監(jiān)聽機制
2.1 SpringBoot中的事件發(fā)布者
接著看run
方法
//根據(jù)args獲取所有SpringApplicationRunListeners監(jiān)聽器
SpringApplicationRunListeners listeners = getRunListeners(args);
//這段代碼大家應該已經(jīng)熟悉了,獲取SpringApplicationRunListeners擴展
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[]{SpringApplication.class, String[].class};
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
以上代碼通過Class<?>[] types = new Class<?>[]{SpringApplication.class, String[].class};
類型加載對應的監(jiān)聽器,并創(chuàng)建SpringApplicationRunListener
實例,看下'SpringApplicationRunListener'的構(gòu)造方法
// 接受一個Log和Collection對象并賦給類成員變量
private final Log log;
private final List<SpringApplicationRunListener> listeners;
SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
this.log = log;
this.listeners = new ArrayList<>(listeners);
}
通過分析,SpringApplicationRunListeners
類的主要作用就是存儲監(jiān)聽器對象集合并發(fā)布各種監(jiān)聽事件,SpringApplicationRunListeners
其本質(zhì)上就是一個事件對象存儲和發(fā)布者,它在SpringBoot
應用啟動的不同時間點委托給ApplicationEventMulticaster(下面有介紹)
發(fā)布不同應用事件類型(ApplicationEvent
)
SpringApplicationRunListeners
會發(fā)布哪些事件呢,看源碼
//首次啟動run方法時立即調(diào)用阳惹。
public void starting() {
for (SpringApplicationRunListener listener : this.listeners) {
listener.starting();
}
}
// 一旦準備好環(huán)境谍失,但在ApplicationContext創(chuàng)建環(huán)境之前調(diào)用 。
public void environmentPrepared(ConfigurableEnvironment environment) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.environmentPrepared(environment);
}
}
// ApplicationContext在創(chuàng)建和準備之后調(diào)用莹汤,但在加載源之前調(diào)用快鱼。
public void contextPrepared(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.contextPrepared(context);
}
}
// 在應用程序上下文加載之后但在刷新之前調(diào)用。
public void contextLoaded(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.contextLoaded(context);
}
}
// 上下文已被刷新纲岭,并且應用程序已啟動抹竹,且CommandLineRunners和ApplicationRunners未被調(diào)用。
public void started(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.started(context);
}
}
// 在run方法完成之前立即調(diào)用止潮,應用上下文已經(jīng)被刷新,并且CommandLineRunners和ApplicationRunners已經(jīng)被調(diào)用
public void running(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.running(context);
}
}
// 在運行應用程序時發(fā)生故障時調(diào)用窃判。
public void failed(ConfigurableApplicationContext context, Throwable exception) {
for (SpringApplicationRunListener listener : this.listeners) {
callFailedListener(listener, context, exception);
}
}
2.2 SpringBoot中的事件類型
查看org.springframework.boot.context.event
包下類共定義了以下事件類型
ApplicationEnvironmentPreparedEvent.java
ApplicationFailedEvent.java
ApplicationPreparedEvent.java
ApplicationReadyEvent.java
ApplicationStartedEvent.java
ApplicationStartingEvent.java
-
SpringApplicationEvent.java
上面類的定義和作用比較簡答,可查看類注釋
SpringApplicationEvent
類是SpringBoot事件類的抽象基類,查看其類圖關(guān)系,可以發(fā)現(xiàn),該類也是通過繼承java.util.EventObject
實現(xiàn)的
注意:在該包下還有EventPublishingRunListener
接口,用來發(fā)布各種事件,下面我們會詳細分析
此處要注意SpringApplicationRunListeners
和SpringApplicationRunListener
的關(guān)系
-
SpringApplicationRunListeners
中包含了private final List<SpringApplicationRunListener> listeners
集合 - 真正負責事件發(fā)布的是
SpringApplicationRunListener
-
SpringApplicationRunListener
中又維護了SimpleApplicationEventMulticaster
對象,并通過該對象將事件廣播給各個監(jiān)聽器
2.3 SpringBoot中的事件監(jiān)聽器
打開spring.factories
文件查看
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
2.4 SpringBoot中的事件注冊
SpringBoot
的事件,監(jiān)聽器,以及其發(fā)布者,事件廣播器已經(jīng)有所介紹,并且SpringBoot
委托ApplicationEventMulticaster
進行事件廣播,那么,事件是何時注冊到廣播器的呢,通過下面的代碼逐步調(diào)用,實例化EventPublishingRunListener
對象時會將事件注冊到廣播器
//入口是SpringApplication的run方法
SpringApplicationRunListeners listeners = getRunListeners(args);
-getRunListeners(String[] args)
-return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
-getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args)
-T instance = (T) BeanUtils.instantiateClass(constructor, args);
打開EventPublishingRunListener
源碼
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
//創(chuàng)建SimpleApplicationEventMulticaster對象
//SimpleApplicationEventMulticaster-->AbstractApplicationEventMulticaster-->ApplicationEventMulticaster
this.initialMulticaster = new SimpleApplicationEventMulticaster();
//從application對象中獲取所有已經(jīng)加載的Listener對象,循環(huán)并注冊至initialMulticaster對象
for (ApplicationListener<?> listener : application.getListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
2.5 SpringBoot中的事件發(fā)布與監(jiān)聽
EventPublishingRunListener
作為事件的發(fā)布者已經(jīng)在前面初始化,當程序執(zhí)行到listeners.starting();
時,調(diào)用了
SpringApplicationRunListeners
的starting()
方法
//首次啟動run方法時立即調(diào)用。
public void starting() {
for (SpringApplicationRunListener listener : this.listeners) {
listener.starting();
}
}
this.listeners
集合中包含了EventPublishingRunListener
實例,那么這里將要調(diào)用其starting()
方法
private final SimpleApplicationEventMulticaster initialMulticaster;
@Override
// 廣播ApplicationStartingEvent事件
public void starting() {
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
SimpleApplicationEventMulticaster繼承了AbstractApplicationEventMulticaster,其作用如下
將所有事件多播到所有已注冊的偵聽器喇闸,將其留給偵聽器以忽略他們不感??興趣的事件袄琳。偵聽器通常instanceof 會對傳入的事件對象執(zhí)行相應的檢查。
默認情況下燃乍,在調(diào)用線程中調(diào)用所有偵聽器唆樊。這允許惡意偵聽器阻塞整個應用程序的危險,但增加了最小的開銷刻蟹。指定備用任務(wù)執(zhí)行程序以使偵聽器在不同的線程中執(zhí)行逗旁,例如從線程池中執(zhí)行。
繼續(xù)跟蹤源碼
@Override
public void multicastEvent(ApplicationEvent event) {
multicastEvent(event, resolveDefaultEventType(event));
}
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
//getApplicationListeners(event, type)-->通過給定的事件類型,返回監(jiān)聽器集合
//此處獲取到的監(jiān)聽器有LoggingApplicationListener,
//DelegatingApplicationListener,
//LiquibaseServiceLocatorApplicationListener等監(jiān)聽器,
//以LoggingApplicationListener為例繼續(xù)debug跟蹤,至于符合獲取對應的監(jiān)聽器,放在下面分析
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
//如果上下文中有線程池則使用線程池調(diào)用
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
//根據(jù)事件執(zhí)行對應的監(jiān)聽器
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
doInvokeListener(listener, event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else {
doInvokeListener(listener, event);
}
}
//執(zhí)行監(jiān)聽器
//-->本例分析的監(jiān)聽器為LoggingApplicationListener
//-->事件為ApplicationStartedEvent
//大家debug時要注意看自己的事件和監(jiān)聽器是什么,代碼跟蹤會進到不同的監(jiān)聽器中
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
String msg = ex.getMessage();
if (msg == null || matchesClassCastMessage(msg, event.getClass().getName())) {
// Possibly a lambda-defined listener which we could not resolve the generic event type for
// -> let's suppress the exception and just log a debug message.
Log logger = LogFactory.getLog(getClass());
if (logger.isDebugEnabled()) {
logger.debug("Non-matching event type for listener: " + listener, ex);
}
}
else {
throw ex;
}
}
}
@Override
//-->LoggingApplicationListener類
//判斷事件類型決定調(diào)用對應的事件
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationStartingEvent) {
//-->被調(diào)用
onApplicationStartingEvent((ApplicationStartingEvent) event);
} else if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
} else if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent((ApplicationPreparedEvent) event);
} else if (event instanceof ContextClosedEvent && ((ContextClosedEvent) event).getApplicationContext().getParent() == null) {
onContextClosedEvent();
} else if (event instanceof ApplicationFailedEvent) {
onApplicationFailedEvent();
}
}
//實例化loggingSystem并將將日志記錄系統(tǒng)重置為限制輸出座咆。
private void onApplicationStartingEvent(ApplicationStartingEvent event) {
this.loggingSystem = LoggingSystem.get(event.getSpringApplication().getClassLoader());
this.loggingSystem.beforeInitialize();
}
至此,已經(jīng)分析了SpringBoot
中的事件類型,監(jiān)聽器類型,事件發(fā)布與監(jiān)聽的過程
繼續(xù)上文未完成分析
2.6 SpringBoot根據(jù)事件類型獲取對應的監(jiān)聽器集合
上面代碼中有getApplicationListeners(event, type)
一句話,SpringBoot
是如何根據(jù)不同的事件來獲取不同的監(jiān)聽器呢,看源碼
//返回與給定事件類型匹配的ApplicationListeners集合痢艺。不匹配的Listeners會盡早被排除在外。
protected Collection<ApplicationListener<?>> getApplicationListeners(ApplicationEvent event, ResolvableType eventType) {
//獲取事件源,并封裝至ListenerCacheKey對象
Object source = event.getSource();
Class<?> sourceType = (source != null ? source.getClass() : null);
ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
// Quick check for existing entry on ConcurrentHashMap...
//嘗試從緩存中獲取事件
ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
if (retriever != null) {
return retriever.getApplicationListeners();
}
//檢查給定類在給定上下文中是否是緩存安全的介陶,即它是由給定的ClassLoader還是由其父類加載堤舒。
if (this.beanClassLoader == null || (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
// 加鎖
synchronized (this.retrievalMutex) {
////搶到鎖之后再做一次判斷,因為有可能在前面BLOCK的時候哺呜,另一個搶到鎖的線程已經(jīng)設(shè)置好了緩存
retriever = this.retrieverCache.get(cacheKey);
if (retriever != null) {
return retriever.getApplicationListeners();
}
//前面都沒有能從緩存中獲取到,則創(chuàng)建ListenerRetriever對象,
retriever = new ListenerRetriever(true);
//根據(jù)事件源檢索對應的監(jiān)聽器,詳解見下面
Collection<ApplicationListener<?>> listeners = retrieveApplicationListeners(eventType, sourceType, retriever);
//加入到retrieverCache緩存中
this.retrieverCache.put(cacheKey, retriever);
//返回監(jiān)聽
return listeners;
}
}
else {
// No ListenerRetriever caching -> no synchronization necessary
return retrieveApplicationListeners(eventType, sourceType, null);
}
}
//根據(jù)事件源檢索對應的監(jiān)聽器,以下例分析
//eventType-->org.springframework.boot.context.event.ApplicationStartingEvent
//sourceType-->class org.springframework.boot.SpringApplication
//retriever-->Helper類舌缤,它封裝一組特定的目標監(jiān)聽器,允許有效地檢索預先過濾的監(jiān)聽器某残。
private Collection<ApplicationListener<?>> retrieveApplicationListeners(
ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable ListenerRetriever retriever) {
List<ApplicationListener<?>> allListeners = new ArrayList<>();
Set<ApplicationListener<?>> listeners;
Set<String> listenerBeans;
synchronized (this.retrievalMutex) {
listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
}
//循環(huán)上下文初始化加載的所有監(jiān)聽器
for (ApplicationListener<?> listener : listeners) {
//判斷監(jiān)聽器是否支持給定的事件,下面詳解
if (supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
retriever.applicationListeners.add(listener);
}
allListeners.add(listener);
}
}
//此段代碼不知道干啥的...
if (!listenerBeans.isEmpty()) {
BeanFactory beanFactory = getBeanFactory();
for (String listenerBeanName : listenerBeans) {
try {
Class<?> listenerType = beanFactory.getType(listenerBeanName);
if (listenerType == null || supportsEvent(listenerType, eventType)) {
ApplicationListener<?> listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class);
if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
retriever.applicationListenerBeans.add(listenerBeanName);
}
allListeners.add(listener);
}
}
}
catch (NoSuchBeanDefinitionException ex) {
// Singleton listener instance (without backing bean definition) disappeared -
// probably in the middle of the destruction phase
}
}
}
//排序并返回
AnnotationAwareOrderComparator.sort(allListeners);
return allListeners;
}
//判斷監(jiān)聽器是否支持給定的事件
protected boolean supportsEvent(ApplicationListener<?> listener, ResolvableType eventType, @Nullable Class<?> sourceType) {
//如果監(jiān)聽器是GenericApplicationListener實例,則直接返回
//否則創(chuàng)建GenericApplicationListenerAdapter實例
//GenericApplicationListenerAdapter-->事件適配器
GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ?
(GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener));
return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType));
}
至此,我們已經(jīng)分析了SpringBoot
根據(jù)事件類型獲取對應的監(jiān)聽器集合,細節(jié)還很多,還有很多沒分析到的地方,篇幅限制,不能把所有的代碼都粘貼出來,大家諒解!