上一篇文章深入理解事件发布监听机制,小编实现了一个基于jdk的事件监听处理的小程序,虽然功能简单,但是却很详细的说明了事件监听处理的原理。在Spring中也有完整的事件监听处理机制,原理和jdk的事件处理流程大概是相同的,只不过Spring在实现的过程中,有一些特殊的细节处理。比如:事件类型和监听器对应关系的缓存,事件处理的异步化,异常处理器定义。
下面我们基于Spring中的事件处理机制实现一个和上篇文章相同的事件监听处理程序,然后,从源码角度看一下Spring是如何实现事件监听处理机制的。
案例解析
为了更直观的了解Spring的事件监听处理机制,我们先基于Spring重新实现上篇文章中的任务状态变更事件的监听处理应用。
定义事件
public class TaskInitEvent extends ApplicationEvent {
public TaskInitEvent(Object source) {
super(source);
}
@Override
public String toString() {
return "TaskInitEvent{" +
"source=" + source +
'}';
}
}
在spring中,定义的事件要继承 ApplicationEvent,其实ApplicationEvent同样继承了 EventObject只不过扩展了一个 timestamp时间戳字段。
这里只为了说明spring事件处理的原理,对于另外两个事件 TaskProcessEvent,TaskFinishEvent暂不做详细定义。
定义事件监听器
@Component
public class TaskInitEventListener implements ApplicationListener<TaskInitEvent> {
@Override
public void onApplicationEvent(TaskInitEvent event) {
System.out.println("处理初始化事件: "+ event.toString());
}
}
在spring中自定义的事件监听器,要实现ApplicationListener接口,而ApplicationListener继承了EventListener接口,也就是说,Spring中的事件监听处理机制的实现也是遵从jdk中事件处理规范的。
定义事件发布器
在sprig中我们无需定义事件发布器,ApplicationContext具备发布事件的能力,可以将ApplicationContext作为事件发布器即可。事件发布程序如下:
@SpringBootApplication
public class SpringEventApp {
public static void main(String[] args) {
ApplicationContext applicationContext = SpringApplication.run(SpringEventApp.class, args);
Task initTask = new Task("init");
TaskInitEvent initEvent = new TaskInitEvent(initTask);
applicationContext.publishEvent(initEvent);
}
}
在控制台可以看到如下图的输出:
源码解析
我们都知道事件处理主要有三个组件构成:事件发送器,事件定义,事件监听器。
接下来,我们就Spring源码,看一下这三个组件是如何加载,以及如何根据事件找到对应的监听器,进而完成事件处理的。
在看源码前,我这里先提出几个问题,带着问题看代码才能更有目的性:
1.ApplicationConext为什么具有事件发布的能力,可以发布事件?
2.事件监听器是如何加载?
3.发布的事件是如何找到对应的事件监听器的?
publisher如何加载的
上面说ApplicationContext具有事件发布的能力,其实事件并不是ApplicationContext,真正的事件发布器是ApplicationEventMulticaster发布的。它定义在AbstractApplicationContext中,并在ApplicationContext容器启动的时候进行初始化。具体的初始化逻辑,就定义在大名鼎鼎的refrsh()方法中:
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
prepareBeanFactory(beanFactory);
try {
...
// 初始化事件发布器.
initApplicationEventMulticaster();
...
//注册事件监听器.
registerListeners();
...
}
catch (BeansException ex) {
...
}
finally {
...
}
}
}
而在 initApplicationEventMulticaster
方法中完成了 applicationEventMulticaster的初始化,具体如下:
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isDebugEnabled()) {
logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
"': using default [" + this.applicationEventMulticaster + "]");
}
}
}
这里会根据核心容器beanFactory中是否有id为applicationEventMulticaster的bean分两种情况:
1.容器中已有id为applicationEventMulticaster的bean直接从容器缓存获取或是创建该bean实例,并交由成员变量applicationEventMulticaster保存。当用户自定义了事件发布器并向容器注册时会执行该流程。
2.容器中不存在applicationEventMulticaster的bean这是容器默认的执行流程,会创建一个SimpleApplicationEventMulticaster。
到这里完成了事件发布器的初始化,我们知道事件监听处理机制是一种观察者模式的应用,事件发布器会持有所有的事件监听器的实例,那么这些监听器的实例从哪里注册进去的呢?
listener是如何加载的
注册事件监听器的流程,同样在spring的IOC的refresh方法中,具体代码如下:
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 向beanFactory中添加 BeanPostProcesser ApplicationListenerDetector,这个BeanPostProcessor将实现ApplicationListener的bean进行注册。
prepareBeanFactory(beanFactory);
try {
...
// 初始化事件发布器.
initApplicationEventMulticaster();
...
//注册事件监听器.
registerListeners();
...
}
catch (BeansException ex) {
...
}
finally {
...
}
}
}
其中BeanPostProcessor ApplicationListenerDetector会将所有ApplicationListener的bean添加到 AbstractApplicationContext的 applicationListeners 集合中,具体代码如下:
public Object postProcessAfterInitialization(Object bean, String beanName) {
//过滤 ApplicationListener
if (bean instanceof ApplicationListener) {
Boolean flag = this.singletonNames.get(beanName);
if (Boolean.TRUE.equals(flag)) {
// 将ApplicationListener添加到 AbstractApplicationContext的 applicationListeners 中
this.applicationContext.addApplicationListener((ApplicationListener<?>) bean);
}
else if (Boolean.FALSE.equals(flag)) {
...
}
return bean;
}
同时方法registerListeners会将AbstractApplicationContext中的 applicationListeners 注册到 ListenerRetriever的 applicationListeners集合中,完成listener的注册,具体注册逻辑如下:
protected void registerListeners() {
// Register statically specified listeners first.
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let post-processors apply to them!
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
// Publish early application events now that we finally have a multicaster...
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (earlyEventsToProcess != null) {
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}
到这里完成了 事件监听器的注册。
现在有了事件发布器和事件监听器,接下来,我们就来研究下,事件发布器是如何将事件发布出去的,以及对应的事件监听器是如何处理被发布的事件。
event是如何处理的
事件发布的主要流程如下(为了更加清晰的理解事件发布的流程,这里只展示一些关键路径的上的代码) 首先AbstractApplicationContext的publishEvent方法,是事件发布的入口:
public void publishEvent(ApplicationEvent event) {
publishEvent(event, null);
}
publishEvent方法具体如下:
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
...
// 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);
}
...
}
在调用 multicastEvent方法时,会将事件的类型作为入参:
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
调用resolveDefaultEventType(event)方法,获取事件的事件类型信息,通过getApplicationListeners(event, type)方法得到所有和该事件类型匹配的事件监听器,对于如何找到和事件匹配的listeners,读者可以自行参考 AbstractApplicationEventMulticaster中的方法:
protected boolean supportsEvent(
ApplicationListener<?> listener, ResolvableType eventType, @Nullable Class<?> sourceType)
对于匹配的每一个监听器,视事件发布器内是否设置了任务执行器实例Executor,决定以何种方式对监听器的监听方法进行回调。
若执行器实例Executor未设置,则进行同步回调,即在当前线程执行监听器的回调方法
若用户设置了Executor实例(通常而言是线程池),则会进行异步回调,监听器的监听方法会交由线程池中的线程去执行。
到这里事件的发布和处理的流程就说完了。
自定义事件发送器
上面说有说Spring的 SimpleApplicationEventMulticaster是支持异步事件处理的,但是默认情况只支持同步的处理,因为Spring在初始化 SimpleApplicationEventMulticaster时,并没有对属性 taskExecutor和errorHandler进行初始化。如果我们想要实现事件处理异步化和自定义异常处理的化,可以通过自定义事件发布器实现;
@Configuration
public class AsyncTaskConfig {
public SimpleAsyncTaskExecutor simpleAsyncTaskExecutor() {
return new SimpleAsyncTaskExecutor();
}
public ErrorHandler errorHandler(){
return new ErrorHandler() {
@Override
public void handleError(Throwable t) {
System.err.println("事件监听器出错:"+t.getMessage());
}
};
}
@Bean
public SimpleApplicationEventMulticaster applicationEventMultica1ster() {
SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
simpleApplicationEventMulticaster.setTaskExecutor(simpleAsyncTaskExecutor());
simpleApplicationEventMulticaster.setErrorHandler(errorHandler());
return simpleApplicationEventMulticaster;
}
}
通过修改Listener的事件处理逻辑,让事件处理抛出异常,对ErrorHander进行验证,并打印出事件处理逻辑所在的线程名称:
@Component
public class TaskInitEventListener implements ApplicationListener<TaskInitEvent> {
@Override
public void onApplicationEvent(TaskInitEvent event) {
System.out.println("处理初始化事件: "+ event.toString()+" thread name:"+Thread.currentThread().getName());
}
}
执行结果如下图: