Spring事件机制分为事件发布器(EventPublisher)、事件监听器(EventListener)和事件多播器(ApplicationEventMulticaster)。Spring事件机制对应常用设计模式之观察者模式,主要就是用来解耦。
Spring 的 ApplicationContext 提供了支持事件和代码中监听器的功能。我们可以创建 bean 用来监听在 ApplicationContext 中发布的事件。ApplicationEvent 类和在ApplicationContext 接口中处理的事件,如果一个 bean 实现了 ApplicationListener 接口,当一个 ApplicationEvent 被发布以后,bean 会自动被通知。
需要实现ApplicationEventPublisherAware这个Aware接口,广播事件需要利用到applicationEventPublisher。用户发布的事件类型可以是:
@Component
public class StringEventPublish implements ApplicationEventPublisherAware {
private ApplicationEventPublisher applicationEventPublisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
public void saySomething(String msg){
System.out.println("发布者线程名:" + Thread.currentThread().getName());
applicationEventPublisher.publishEvent(msg);
}
}
事件监听者需要实现ApplicationListener接口,由于监听的是String类型的事件会被封装成PayloadApplicationEvent,所以此处类型是PayloadApplicationEvent。
@Component
public class StringListener implements ApplicationListener<PayloadApplicationEvent<String>> {
public void onApplicationEvent(PayloadApplicationEvent event) {
Object msg = event.getPayload();
System.out.println("监听器线程名:" + Thread.currentThread().getName());
System.out.println("ListenerA receive:" + msg);
}
}
关于发布出去的事件,哪些监听者会监听到?
Spring 在创建默认的事件多播器 SimpleApplicationEventMulticaster 时,taskExecutor 属性默认是null,所以默认情况下所有的监听器的 onApplicationEvent 是直接在当前线程(事件发布者所在线程)中调用。如果 onApplicationEvent 有阻塞操作也会导致事件发布者被阻塞,后续的其他监听器也会被阻塞无法调用。
我们可以为默认事件多播器设置 taskExecutor 属性,从而达到异步监听的目的。
XML 配置方式:
<bean id="executorService" class="java.util.concurrent.Executors" factory-method="newScheduledThreadPool">
</bean>
<bean id="applicationEventMulticaster" class="org.springframework.context.event.SimpleApplicationEventMulticaster">
<property name="taskExecutor" ref="executorService">
</property>
</bean>
使用注解的话,可以在Spring应用启动完成后进行设置:
@Component
@AllArgsConstructor
public class EventMulticasterConfig implements ApplicationRunner {
private final SimpleApplicationEventMulticaster simpleApplicationEventMulticaster;
@Override
public void run(ApplicationArguments args){
ExecutorService executorService = Executors.newScheduledThreadPool(10);
simpleApplicationEventMulticaster.setTaskExecutor(executorService);
}
}
可以看到事件多播器的类图,其默认实现只有一个 SimpleApplicationEventMulticaster。
所以我们先来看下多播器是在哪里进行初始化的,在 AbstractApplicationContext.refresh() 方法中进行刷新时有调用 initApplicationEventMulticaster 方法进行初始化容器事件广播器,并放入至上下文的applicationEventMulticaster 属性中。所以我们来看下其初始化代码:
protected void initApplicationEventMulticaster() {
//获取beanFactory
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
//从beanFactory中获取事件广播器
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
//如果有,则赋值给applicationEventMulticaster
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isTraceEnabled()) {
logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {
//如果没有,则创建一个SimpleApplicationEventMulticaster
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
//讲创建的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() + "]");
}
}
}
在 IOC 容器初始化完成后,事件广播器也初始化完成。我们就可以直接调用 publishEvent 进行发布事件,就根据这个方法看看是如何实现的。跳转到 AbstractApplicationContext.publishEvent() 方法,源码如下:
public void publishEvent(Object event) {
publishEvent(event, null);
}
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
// 判断事件类型是否为ApplicationEvent,如果不是则封装成PayloadApplicationEvent
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
}
}
// 在 IOC 初始化过程中的早期事件需要立即进行发布
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);
}
}
}
跳转到 SimpleApplicationEventMulticaster.multicastEvent() ,将给定的应用程序事件多播到适当的侦听器上。我们也可以上看上面所说的关于线程池的判断,所以需要设置 taskExecutor 进行异步化监听处理。
public void multicastEvent(ApplicationEvent event) {
//将给定的应用程序事件多播到适当的侦听器
multicastEvent(event, resolveDefaultEventType(event));
}
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
// 获取到事件对应的分解类型
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
// 获取到多播器的当前线程池
Executor executor = getTaskExecutor();
// 获取当前应用中与给定事件类型匹配的ApplicationListeners的监听器集合,不符合的监听器会被排除在外。再循环执行
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
// 如果线程池不为空,则通过线程池异步执行
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
// 否则由当前线程执行
invokeListener(listener, event);
}
}
}
invokeListener 方法比较简单,就是调用监听器的监听方法 onApplicationEvent 进行处理事件。
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);
}
}
@SuppressWarnings({"rawtypes", "unchecked"})
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())) {
Log logger = LogFactory.getLog(getClass());
if (logger.isTraceEnabled()) {
logger.trace("Non-matching event type for listener: " + listener, ex);
}
}
else {
throw ex;
}
}
}
还有一个重要的方法是 getApplicationListeners ,如何返回与给定事件类型匹配的ApplicationListeners的集合,并将不匹配的监听器会尽早被排除在外。
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);
// Quick check for existing entry on ConcurrentHashMap...
ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
if (retriever != null) {
return retriever.getApplicationListeners();
}
if (this.beanClassLoader == null ||
(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
// 完全同步构建和缓存ListenerRetriever
synchronized (this.retrievalMutex) {
retriever = this.retrieverCache.get(cacheKey);
if (retriever != null) {
return retriever.getApplicationListeners();
}
retriever = new ListenerRetriever(true);
//实际检索给定事件和源类型的应用程序监听器,将过滤后的监听器集合进行返回
Collection<ApplicationListener<?>> listeners =
retrieveApplicationListeners(eventType, sourceType, retriever);
this.retrieverCache.put(cacheKey, retriever);
return listeners;
}
}
else {
// No ListenerRetriever caching -> no synchronization necessary
return retrieveApplicationListeners(eventType, sourceType, null);
}
}
继续根据 retrieveApplicationListeners 方法,进行实际检索给定事件和源类型的应用程序监听器的实现。通过 supportsEvent 进行判断监听器是否支持给定事件,源码如下:
private Collection<ApplicationListener<?>> retrieveApplicationListeners(
ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable ListenerRetriever retriever) {
List<ApplicationListener<?>> allListeners = new ArrayList<>();
//当前应用中的监听器集合
Set<ApplicationListener<?>> listeners;
//当前应用中的监听器BeanName集合
Set<String> listenerBeans;
synchronized (this.retrievalMutex) {
listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
}
for (ApplicationListener<?> listener : listeners) {
// 判断监听器是否支持给定事件
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 {
//根据监听器的BeanName获取到对应类型
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) {
if (beanFactory.isSingleton(listenerBeanName)) {
retriever.applicationListeners.add(listener);
}
else {
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);
if (retriever != null && retriever.applicationListenerBeans.isEmpty()) {
retriever.applicationListeners.clear();
retriever.applicationListeners.addAll(allListeners);
}
return allListeners;
}
我们已经完成对 Spring 事件机制原理的解析,主要流程如下:
Spring 提供了以下 5 中标准的事件,我们可以注册响应的监听器进行处理该事件。
参考:
https://www.jianshu.com/p/dcbe8f0afbdb