前言
前一段時間,剛剛接手一個項目,項目中看到使用的spring的事件監(jiān)聽機制,加上之前自己看spring源碼的時候也對spring listener 有點影像,于是就重新追一追源碼,理一理spring 事件監(jiān)聽機制的工作原理
案例
- 自定義event
- 自定義listener
- 測試推送
- 測試結(jié)果
一個非常簡單的案例,通過定義事件,監(jiān)聽,推送實現(xiàn)了簡單的事件監(jiān)聽的流程。
原理分析
- 推送事件
通過案例可以看到我們通過
ApplicationContext.publishEvent方法來推送事件,實際是其子類 AbstractApplicationContext 實現(xiàn)的發(fā)送操作
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
// Decorate event as an ApplicationEvent if necessary
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
}
}
// 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);
}
// Publish event via parent context as well...
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}
通過源碼可以看到發(fā)送事件最終是交個
ApplicationEventMulticaster來推送。
那么
ApplicationEventMulticaster對象是怎么來的呢,它里面具體是如何推送的呢?我們接著往下看。
讀過spring源碼的同學一定對
AbstractApplicationContext中的refresh方法很熟悉。這是spring源碼的核心部分,而在其中有一步
就是來構(gòu)造
ApplicationEventMulticaster。
參照源碼,我們并沒有定義和注冊
applicationEventMulticaster,所以就會使用SimpleApplicationEventMulticaster。
在multicastEvent中
1,首先是ResolvableType的確認
可以看到最后是通過Event的class對象作為ResolvableType的屬性,后面匹配Listener的時候就是使用這個。
2,通過ResolvableType獲取適合的Listener
protected Collection<ApplicationListener<?>> getApplicationListeners(
ApplicationEvent event, ResolvableType eventType) {
Object source = event.getSource();
Class<?> sourceType = (source != null ? source.getClass() : null);
ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
// Potential new retriever to populate
CachedListenerRetriever newRetriever = null;
// Quick check for existing entry on ConcurrentHashMap
CachedListenerRetriever existingRetriever = this.retrieverCache.get(cacheKey);
if (existingRetriever == null) {
// Caching a new ListenerRetriever if possible
if (this.beanClassLoader == null ||
(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
newRetriever = new CachedListenerRetriever();
existingRetriever = this.retrieverCache.putIfAbsent(cacheKey, newRetriever);
if (existingRetriever != null) {
newRetriever = null; // no need to populate it in retrieveApplicationListeners
}
}
}
if (existingRetriever != null) {
Collection<ApplicationListener<?>> result = existingRetriever.getApplicationListeners();
if (result != null) {
return result;
}
// If result is null, the existing retriever is not fully populated yet by another thread.
// Proceed like caching wasn't possible for this current local attempt.
}
return retrieveApplicationListeners(eventType, sourceType, newRetriever);
}
這邊這種使用緩存機制的代碼,可以在spring源碼中多次看到,所以大家可以學習這種方式,提高性能。
retrieveApplicationListeners方法比較大,我這邊只截取了部分,可以看到是通過eventType
和sourceType來獲取適合的listener。
3,接下來就是調(diào)用invokeListener方法來實際操作,這邊可以看到可以通過Executor
來實現(xiàn)異步操作。當然比較好的方式還是自己配置線程池,這樣可以通過前綴區(qū)分出業(yè)務。
至此可以看到,調(diào)用的是對應listener的onApplicationEvent。