Spring 之 事件机制详解

概念

Spring事件机制分为事件发布器(EventPublisher)、事件监听器(EventListener)和事件多播器(ApplicationEventMulticaster)。Spring事件机制对应常用设计模式之观察者模式,主要就是用来解耦。
Spring 的 ApplicationContext 提供了支持事件和代码中监听器的功能。我们可以创建 bean 用来监听在 ApplicationContext 中发布的事件。ApplicationEvent 类和在ApplicationContext 接口中处理的事件,如果一个 bean 实现了 ApplicationListener 接口,当一个 ApplicationEvent 被发布以后,bean 会自动被通知。

使用

事件发布器

需要实现ApplicationEventPublisherAware这个Aware接口,广播事件需要利用到applicationEventPublisher。用户发布的事件类型可以是:

  1. 用户可以继承ApplicationEvent从而自定义Event类型
  2. 也可以使用任意Object类型,但是如果event真实类型不是ApplicationEvent的话,那么event会被封装成PayloadApplicationEvent
@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);
   }
}

关于发布出去的事件,哪些监听者会监听到?

  1. 发布的事件类型是ApplicationEvent的实现类A,那么所有监听者的onApplicationEvent的参数类型是A或者A的子类都会收到事件。
  2. 发布的事件类型是不是ApplicationEvent类型,类型是B。这种情况下,最终事件会被包装成PayloadApplicationEvent<B>,那么所有监听者方法onApplicationEvent的参数是PayloadApplicationEvent<B>的监听者会收到。假设有C是B的父类,且有一个监听者X监听PayloadApplicationEvent<C>,那X是收不到PayloadApplicationEvent类型的事件的。

事件多播器

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 事件机制原理的解析,主要流程如下:

  1. 初始化事件多播器
  2. 事件发布器发布事件
  3. 事件多播器接收到事件后,检索监听器集合得到需要执行事件处理的监听器
  4. 循环调用具体监听器方法处理事件

Spring 提供了以下 5 中标准的事件,我们可以注册响应的监听器进行处理该事件。

  1. 上下文更新事件(ContextRefreshedEvent):在调用ConfigurableApplicationContext 接口中的refresh()方法时被触发。
  2. 上下文开始事件(ContextStartedEvent):当容器调用ConfigurableApplicationContext的Start()方法开始/重新开始容器时触发该事件。
  3. 上下文停止事件(ContextStoppedEvent):当容器调用ConfigurableApplicationContext的Stop()方法停止容器时触发该事件。
  4. 上下文关闭事件(ContextClosedEvent):当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例Bean都被销毁。
  5. 请求处理事件(RequestHandledEvent):在Web应用中,当一个http请求(request)结束触发该事件。

参考:
https://www.jianshu.com/p/dcbe8f0afbdb

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章