1. 什么是事件监听机制
在讲解事件监听机制前,我们先回顾下设计模式中的观察者模式,因为事件监听机制可以说是在典型观察者模式基础上的进一步抽象和改进。我们可以在 JDK 或者各种开源框架比如 Spring 中看到它的身影,从这个意义上说,事件监听机制也可以看做一种对传统观察者模式的具体实现,不同的框架对其实现方式会有些许差别。
典型的观察者模式将有依赖关系的对象抽象为了观察者和主题两个不同的角色,多个观察者同时观察一个主题,两者只通过抽象接口保持松耦合状态,这样双方可以相对独立的进行扩展和变化:比如可以很方便的增删观察者,修改观察者中的更新逻辑而不用修改主题中的代码。但是这种解耦进行的并不彻底,这具体体现在以下几个方面:
- 1.抽象主题需要依赖抽象观察者,而这种依赖关系完全可以去除。
- 2.主题需要维护观察者列表,并对外提供动态增删观察者的接口,
- 3.主题状态改变时需要由自己去通知观察者进行更新。
我们可以把主题(Subject)替换成事件(Event),把对特定主题进行观察的观察者(Observer)替换成对特定事件进行监听的监听器(EventListener),而把原有主题中负责维护主题与观察者映射关系以及在自身状态改变时通知观察者的职责从中抽出,放入一个新的角色事件发布器(EventPublisher)中,事件监听模式的轮廓就展现在了我们眼前,如下图所示:

常见事件监听机制的主要角色如下:
- 事件:对应于观察者模式中的主题。
- 事件监听器:对应于观察者模式中的观察者。监听器监听特定事件,并在内部定义了事件发生后的响应逻辑。
- 事件发布器:事件监听器的容器,对外提供发布事件和增删事件监听器的接口,维护事件和事件监听器之间的映射关系,并在事件发生时负责通知相关监听器。
Spring 框架对事件的发布与监听提供了相对完整的支持,它扩展了JDK中对自定义事件监听提供的基础框架,并与 Spring 的 IOC 特性作了整合,使得用户可以根据自己的业务特点进行相关的自定义,并依托 Spring 容器方便的实现监听器的注册和事件的发布。
2. Spring 容器对事件监听机制的支持
Spring 容器,具体而言是ApplicationContext
接口定义的容器提供了一套相对完善的事件发布和监听框架,其遵循了JDK 中的事件监听标准,并使用容器来管理相关组件,使得用户不用关心事件发布和监听的具体细节,降低了开发难度也简化了开发流程。下面看看对于事件监听机制中的各主要角色,Spring 框架中是如何定义的,以及相关的类体系结构:
-
事件
Spring 为容器内事件定义了一个抽象类ApplicationEvent
,该类继承了JDK 中的事件基类EventObject
。因而自定义容器内事件除了需要继承ApplicationEvent
之外,还要传入事件源作为构造参数:
public abstract class ApplicationEvent extends EventObject {
/** use serialVersionUID from Spring 1.2 for interoperability */
private static final long serialVersionUID = 7099057708183571937L;
/** System time when the event happened */
private final long timestamp;
/**
* Create a new ApplicationEvent.
* @param source the object on which the event initially occurred (never {@code null})
*/
public ApplicationEvent(Object source) {
super(source);
this.timestamp = System.currentTimeMillis();
}
/**
* Return the system time in milliseconds when the event happened.
*/
public final long getTimestamp() {
return this.timestamp;
}
}
- 事件监听器
Spring 定义了一个ApplicationListener
接口作为事件监听器的抽象,接口定义如下:
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
/**
* Handle an application event.
* @param event the event to respond to
*/
void onApplicationEvent(E event);
}
- 该接口继承了 JDK 中表示事件监听器的标记接口
EventListener
,内部只定义了一个抽象方法onApplicationEvent(evnt)
,当监听的事件在容器中被发布,该方法将被调用。 - 同时,该接口是一个泛型接口,其实现类可以通过传入泛型参数指定该事件监听器要对哪些事件进行监听。这样有什么好处?这样所有的事件监听器就可以由一个事件发布器进行管理,并对所有事件进行统一发布,而具体的事件和事件监听器之间的映射关系,则可以通过反射读取泛型参数类型的方式进行匹配,稍后我们会对原理进行讲解。
- 最后,所有的事件监听器都必须向容器注册,容器能够对其进行识别并委托容器内真正的事件发布器进行管理。
- 事件发布器
ApplicationContext
接口继承了ApplicationEventPublisher
接口,从而提供了对外发布事件的能力。
那么是否可以说ApplicationContext
即容器本身就担当了事件发布器的角色呢?其实这是不准确的,容器本身仅仅是对外提供了事件发布的接口,真正的工作其实是委托给了具体容器内部一个ApplicationEventMulticaster
对象,其定义在AbstractApplicationContext
抽象类内部,如下所示:
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
...
/** Helper class used in event publishing */
@Nullable
private ApplicationEventMulticaster applicationEventMulticaster;
...
}
所以,真正的事件发布器是ApplicationEventMulticaster
,这是一个接口,定义了事件发布器需要具备的基本功能:管理事件监听器以及发布事件。其默认实现类是
SimpleApplicationEventMulticaster
,该组件会在容器启动时被自动创建,并以单例的形式存在,管理了所有的事件监听器,并提供针对所有容器内事件的发布功能。
3. 基于 Spring 实现自定义事件与监听
想象我们正在做一个任务调度系统,我们需要把任务提交到集群中并监控任务的执行状态,当任务执行完毕(成功或者失败),除了必须对数据库进行更新外,还可以执行一些额外的工作:比如将任务执行结果以邮件的形式发送给用户。这些额外的工作后期还有较大的变动可能:比如还需要以短信的形式通知用户,对于特定的失败任务需要通知相关运维人员进行排查等等,所以不宜直接写死在主流程代码中。最好的方式自然是以事件监听的方式动态的增删对于任务执行结果的处理逻辑。为此我们可以基于 Spring 实现自定义事件与监听,打造一个能够对任务执行结果进行监听的弹性系统。
-
自定任务结束事件
定义一个任务结束事件TaskFinishedEvent
,该类继承抽象类ApplicationEvent
来遵循容器事件规范。
package com.tongbanjie.application.event;
import org.springframework.context.ApplicationEvent;
/**
* @author Lu. Yan
* @create 2019-06-26
*/
public class TaskFinishedEvent extends ApplicationEvent {
/**
* Create a new ApplicationEvent.
*
* @param source the object on which the event initially occurred (never {@code null})
*/
public TaskFinishedEvent(Object source) {
super(source);
}
}
- 自定义邮件服务监听器并向容器注册
该类实现了容器事件规范定义的监听器接口ApplicationListener<?>
,通过泛型参数指定对上面定义的任务结束事件进行监听,通过 @Component 注解向容器进行注册。
package com.tongbanjie.application.listener;
import com.tongbanjie.application.event.TaskFinishedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
/**
* @author Lu. Yan
* @create 2019-06-26
*/
@Component
public class MailTaskFinishedListener implements ApplicationListener<TaskFinishedEvent> {
private String emailAddr ="xxxxxx@163.com";
@Override
public void onApplicationEvent(TaskFinishedEvent event) {
System.out.println("Send Email to "+ emailAddr +" Task:"+event.getSource());
}
}
- 发布事件
从上面对 Spring 事件监听机制的类结构分析可知,发布事件的功能定义在ApplicationEventPublisher
接口中,而ApplicationContext
继承了该接口,所以最好的方法是通过实现ApplicationContextAware
接口获取ApplicationContext
实例,然后调用其发布事件方法。
@Component
public class EventPublisher implements ApplicationContextAware {
private ApplicationContext applicationContext;
//发布事件
public void publishEvent(ApplicationEvent event){
applicationContext.publishEvent(event);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext=applicationContext;
}
}
这样就可以在任务结束之后,调用EventPublisher#publishEvent(ApplicationEvent)
方法,来发布TaskFinishedEvent
事件。
4. Spring 事件监听源码解析
Spring 事件监听机制离不开容器 IOC 特性提供的支持,比如容器会自动创建事件发布器,自动识别用户注册的监听器并进行管理,在特定的事件发布后会找到对应的事件监听器并对其监听方法进行回调。Spring 帮助用户屏蔽了关于事件监听机制背后的很多细节,使用户可以专注于业务层面进行自定义事件开发。然而我们还是忍不住对其背后的实现原理进行一番探讨,比如:
- 事件发布器
ApplicationEventMulticaster
是何时被初始化的,初始化过程中都做了什么? - 注册事件监听器的过程是怎样的,容器怎么识别出它们并进行管理?
- 容器发布事件的流程是怎样的?它如何根据发布的事件找到对应的事件监听器,事件和由该事件触发的监听器之间的匹配规则是怎样的?
为了对以上问题进行解答,我们不得不深入源码层面一探究竟。
4.1 初始化事件发布器流程
真正的事件发布器是ApplicationEventMulticaster
,它定义在AbstractApplicationContext
中,并在ApplicationContext
容器启动的时候进行初始化。在容器启动的refrsh()
方法中可以找到初始化事件发布器的入口方法,如下图所示:
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
...
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
/**
* Initialize the ApplicationEventMulticaster.
* Uses SimpleApplicationEventMulticaster if none defined in the context.
* @see org.springframework.context.event.SimpleApplicationEventMulticaster
*/
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// 判断beanFactory里是否定义了id为applicationEventMulticaster的bean,默认是没有的
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 {
//一般情况会走这里,创建一个SimpleApplicationEventMulticaster并交由容器管理
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 + "]");
}
}
}