前一段时间,刚刚接手一个项目,项目中看到使用的spring的事件监听机制,加上之前自己看spring源码的时候也对spring listener 有点影像,于是就重新追一追源码,理一理spring 事件监听机制的工作原理
一个非常简单的案例,通过定义事件,监听,推送实现了简单的事件监听的流程。
通过案例可以看到我们通过
ApplicationContext.publishEvent方法来推送事件,实际是其子类 AbstractApplicationContext 实现的发送操作
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);
}
}
}
通过源码可以看到发送事件最终是交个
ApplicationEventMulticaster来推送。
那么
ApplicationEventMulticaster对象是怎么来的呢,它里面具体是如何推送的呢?我们接着往下看。
读过spring源码的同学一定对
AbstractApplicationContext中的refresh方法很熟悉。这是spring源码的核心部分,而在其中有一步
就是来构造
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,接下来就是调用invokeListener方法来实际操作,这边可以看到可以通过Executor
来实现异步操作。当然比较好的方式还是自己配置线程池,这样可以通过前缀区分出业务。
至此可以看到,调用的是对应listener的onApplicationEvent。