第10章:容器事件和事件监听器
本文内容
1. 设计
在 Spring 中有一个 Event 事件功能,它可以通过 事件的定义、发布、监听,来完成一些自定义的动作。
例如在项目中定义一个用户注册事件,当用户完成注册时,通过事件监听给用户发送优惠卷和短信通知等。使用 事件 功能就能很好的 将基本功能(注册)和对应的策略服务(发送优惠卷、短信通知等)分离开来,降低系统的耦合。当后续再扩展注册服务时(比如添加实名认证、判断用户属性等),就不会影响到依赖于注册成功后执行的动作。
在本章节中需要使用观察者模式来实现上述功能,在对象的状态改变时进行通知其他对象,做出对应的自定义动作。
因此,我们需要定义出 事件类、事件监听类、事件发布类,这些类的功能都需要 结合到 Spring 的 AbstractApplicationContext#refresh() 方法中,用于 处理事件的初始化和注册事件监听器。
整体设计如下:
- 在面向用户的应用上下文
AbstractApplicationContext
中添加相关事件内容,包括:初始化事件发布者、注册事件监听器、发布容器刷新完成事件; - 使用观察者模式定义事件类、监听类、发布类,还要完成一个广播器的功能,将广播器接收到事件推送时,进行分析处理符合监听事件接收者感兴趣的事件,这个功能由
Class#isAssignableFrom()
进行分析判断(后续代码实现中会讲到)。
2. 实现
2.1 定义和实现事件 Event
首先需要定义出 具备事件功能的抽象类 ApplicationEvent,后续所有事件的类都需要继承这个类。想要具备事件功能,可以直接继承 java.util.EventObject
。
java.util.EventObject
是 事件状态对象的基类,它封装了事件源对象 source 以及和事件相关的信息。所有 Java 的事件类都需要继承该类。
接着需要一个 定义事件的类 ApplicationContextEvent,继承自 ApplicationEvent,所有的事件(包括关闭、刷新或自己实现的事件)都需要继承这个类,该类提供一个获取引发事件的 source(即 ApplicationContext)的方法,具体实现如下:
public class ApplicationContextEvent extends ApplicationEvent {
/**
* Constructs a prototypical Event.
*
* @param source the object on which the Event initially occurred
* @throws IllegalArgumentException if source is null
*/
public ApplicationContextEvent(Object source) {
super(source);
}
/**
* 获取 source,即获取引发事件的 ApplicationContext
*/
public final ApplicationContext getApplicationContext() {
return (ApplicationContext) getSource();
}
}
现在,我们在实例化事件类 ApplicationContextEvent 时,就会通过构造函数将 EventObject 类实例化,其中的源对象 source 就是上下文对象 ApplicationContext(包含 beanFactory 资源),当事件发生时,就可以通过 ApplicationContextEvent#getApplicationContext()
(实际上是调用 EventObject#getSource()
)获取到引发事件的 ApplicationContext,然后进行具体的处理。
再添加 Spring 框架自己实现的两个事件类 ContextClosedEvent、ContextRefreshedEvent,继承自 ApplicationContextEvent:
- ContextClosedEvent 用于监听关闭动作;
- ContextRefreshedEvent 用于监听容器刷新动作。
2.2 事件监听器 Listener
定义一个事件监听器接口 ApplicationListener,继承自 java.util.EventListener
,里面提供一个处理 ApplicationEvent 事件的方法 onApplicationEvent(E event)
。由 ApplicationEvent 事件监听器实现该接口。
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
/**
* 处理 ApplicationEvent 事件,具体处理逻辑要用户自定义
* @param event 要响应的事件
*/
void onApplicationEvent(E event);
}
对应上面的事件类,我们需要为其提供对应的监听器,比如 ContextClosedEventListener 和 ContextRefreshedEventListener:
- ContextClosedEventListener 是容器关闭事件监听器,监听动作在容器关闭时执行;
- ContextRefreshedEventListener 时容器刷新事件监听器,监听动作在容器刷新时执行。
注意,上面这两个监听器是用户在需要时才进行实现。
2.3 事件广播器 Multicaster
定义一个事件广播器接口 ApplicationEventMulticaster,提供添加监听器和删除监听器的方法,以及一个广播事件的方法 multicastEvent(ApplicationEvent event)
(将给定的 ApplicationEvent 事件多播给适当的监听器)。
public interface ApplicationEventMulticaster {
/**
* 添加一个监听器
*/
void addApplicationListener(ApplicationListener<?> listener);
/**
* 删除一个监听器
*/
void removeApplicationListener(ApplicationListener<?> listener);
/**
* 将给定的 ApplicationEvent 事件多播给合适的监听器
*/
void multicastEvent(ApplicationEvent event);
}
再定义一个 AbstractApplicationEventMulticaster 抽象类,实现 ApplicationEventMulticaster 接口,在这个类中可以实现一些基本功能,避免所有直接实现接口的类还需要处理细节。具体的功能实现如下:
public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster, BeanFactoryAware {
// 提供一个存储应用监听器的容器
public final Set<ApplicationListener<ApplicationEvent>> applicationListeners = new LinkedHashSet<>();
private BeanFactory beanFactory;
@Override
public void addApplicationListener(ApplicationListener<?> listener) {
applicationListeners.add((ApplicationListener<ApplicationEvent>) listener);
}
@Override
public void removeApplicationListener(ApplicationListener<?> listener) {
applicationListeners.remove(listener);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
/**
* 摘取符合广播事件的所有监听器,具体过滤动作在 supportsEvent 方法中
* @param event 事件
* @return 符合广播事件的所有监听器
*/
protected Collection<ApplicationListener> getApplicationListeners(ApplicationEvent event) {
LinkedList<ApplicationListener> allListeners = new LinkedList<>();
for (ApplicationListener<ApplicationEvent> listener : applicationListeners) {
if (supportsEvent(listener, event)) allListeners.add(listener);
}
return allListeners;
}
/**
* 判断监听器是否对该事件感兴趣
* @param applicationListener 监听器
* @param event 事件
* @return 监听器是否对该事件感兴趣,是返回 true
*/
protected boolean supportsEvent(ApplicationListener<ApplicationEvent> applicationListener, ApplicationEvent event) {
Class<? extends ApplicationListener> listenerClass = applicationListener.getClass();
// // 按照 CglibSubclassingInstantiationStrategy、SimpleInstantiationStrategy 不同的实例化策略,需要判断后获取目标 class
Class<?> targetClass = ClassUtil.isCglibProxyClass(listenerClass) ? listenerClass.getSuperclass() : listenerClass;
Type genericInterface = targetClass.getGenericInterfaces()[0];
Type actualTypeArgument = ((ParameterizedType) genericInterface).getActualTypeArguments()[0];
String className = actualTypeArgument.getTypeName();
Class<?> eventClassName;
try {
eventClassName = Class.forName(className);
} catch (ClassNotFoundException e) {
throw new BeansException("wrong event class name: " + className);
}
// event.getClass() 类是否继承/实现自 eventClassName
return eventClassName.isAssignableFrom(event.getClass());
}
}
最后定义一个 AbstractApplicationEventMulticaster 的简单实现类 SimpleApplicationEventMulticaster,主要是为父类提供 beanFactory,以及实现 multicastEvent(ApplicationEvent event)
方法:
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
// 通过依赖注入,将 beanFactory 注入进来,然后通过 setBeanFactory() 提供给父类
public SimpleApplicationEventMulticaster(BeanFactory beanFactory) {
setBeanFactory(beanFactory);
}
@SuppressWarnings("uncheckd")
@Override
public void multicastEvent(final ApplicationEvent event) {
// 获取该事件的监听者,执行 onApplicationEvent() 处理事件
for (final ApplicationListener listener : getApplicationListeners(event)) {
listener.onApplicationEvent(event);
}
}
}
2.4 定义和实现事件发布者 Publisher
定义一个整个事件的发布接口 ApplicationEventPublisher,提供一个发布事件的方法,所有的事件都需要从这个接口发布出去。
接着在 AbstractApplicationContext#refresh() 中,添加用于处理事件的操作 —— 初始化事件发布者
、注册事件监听器
、发布容器刷新完成事件
,相关实现如下:
public abstract class AbstractApplicationContext
extends DefaultResourceLoader implements ConfigurableApplicationContext {
public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
private ApplicationEventMulticaster applicationEventMulticaster;
@Override
public void refresh() throws BeansException {
// 1. 创建 BeanFactory,并加载 BeanDefinition
refreshBeanFactory();
// 2. 获取 BeanFactory
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// 3. 添加 ApplicationContextAwareProcessor,让继承自 ApplicationContextAware 的 Bean 对象都能感知所属的 ApplicationContext
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
// 4. 在 Bean 实例化之前,执行 BeanFactoryPostProcessor (Invoke factory processors registered as beans in the context.)
invokeBeanFactoryPostProcessors(beanFactory);
// 5. BeanPostProcessor 需要提前于其他 Bean 对象实例化之前执行注册操作
registerBeanPostProcessors(beanFactory);
// 6. 初始化事件发布者
initApplicationEventMulticaster();
// 7. 注册事件监听器
registerListeners();
// 8. 提前实例化单例 Bean 对象
beanFactory.preInstantiateSingletons();
// 9. 完成刷新后,发布刷新完成事件
finishRefresh();
}
/**
* 初始化事件发布者,即实例化 ApplicationEventMulticaster,使 ApplicationContext 具备
* SimpleApplicationEventMulticaster 的功能。然后将实例化的 Bean 对象 applicationEventMulticaster
* 注册进单例容器,供其他地方使用。
*/
private void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, applicationEventMulticaster);
}
/**
* 注册事件监听器:
* 1. 先获取继承/实现了 ApplicationListener 接口的所有监听器
* 2. 将这些监听器加入到监听器容器中
*/
private void registerListeners() {
Collection<ApplicationListener> applicationListeners = getBeansOfType(ApplicationListener.class).values();
for (ApplicationListener listener : applicationListeners) {
applicationEventMulticaster.addApplicationListener(listener);
}
}
/**
* 完成刷新后,发布刷新完成事件
*/
private void finishRefresh() {
publishEvent(new ContextRefreshedEvent(this));
}
@Override
public void publishEvent(ApplicationEvent event) {
applicationEventMulticaster.multicastEvent(event);
}
@Override
public void registerShutdownHook() {
Runtime.getRuntime().addShutdownHook(new Thread(this::close));
}
@Override
public void close() {
// 发布容器关闭事件
publishEvent(new ContextClosedEvent(this));
// 执行销毁单例 bean 的销毁方法
getBeanFactory().destroySingletons();
}
}
- 这里我们需要让 ApplicationContext 继承 ApplicationEventPublisher,这样上下文才拥有发布事件的能力。
2.5 目录结构
到此,事件相关的功能就实现完毕了,下面来看看结构目录的更变(绿色—新增、蓝色—修改):
- 有些小改动就不展示目录结构更变了。
2.6 类结构图
3. 测试
我们创建一个用户自定义的事件和监听器:
创建一个自定义事件,在事件类的构造函数中可以添加自己的想要的入参信息。这个事件类最终会被完整的放到监听里,所以你添加的属性都会被获得到:
public class CustomEvent extends ApplicationEvent { private Long id; private String message; /** * Constructs a prototypical Event. * * @param source the object on which the Event initially occurred * @throws IllegalArgumentException if source is null */ public CustomEvent(Object source, Long id, String message) { super(source); this.id = id; this.message = message; } }
用于监听 CustomEvent 事件的监听器,这里你可以处理自己想要的操作,比如一些用户注册后发送优惠券和短信通知等:
public class CustomEventListener implements ApplicationListener<CustomEvent> { @Override public void onApplicationEvent(CustomEvent event) { System.out.println("收到:" + event.getSource() + "消息,当前时间:" + new Date()); System.out.println("消息:" + event.getId() + ":" + event.getMessage()); } }
另外,还有关于 ContextRefreshedEvent、ContextClosedEvent 对应的监听器:
public class ContextRefreshedEventListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
System.out.println("上下文关闭事件:" + this.getClass().getName());
}
}
public class ContextClosedEventListener implements ApplicationListener<ContextClosedEvent> {
@Override
public void onApplicationEvent(ContextClosedEvent event) {
System.out.println("上下文关闭事件:" + this.getClass().getName());
}
}
接下来,需要在 spring.xml
中配置三个事件监听器:监听刷新事件、监控自定义事件、监听关闭事件。把它们当成 Bean 注册进 Spring 容器中:
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean class="com.run.test.event.ContextRefreshedEventListener"/>
<bean class="com.run.test.event.CustomEventListener"/>
<bean class="com.run.test.event.ContextClosedEventListener"/>
</beans>
测试类如下,我们通过使用 applicationContext 新增加的发布事件接口方法,发布一个自定义事件 CustomEvent,让对应监听器监听,然后执行处理事件:
@Test
public void test_event() {
ClassPathXmlApplicationContext application = new ClassPathXmlApplicationContext("classpath:spring.xml");
// 通过使用 applicationContext 新增加的发布事件接口方法,发布一个自定义事件 CustomEvent,让对应监听器监听,然后执行处理事件
application.publishEvent(new CustomEvent(application, 20000001L, "我是自定义的事件"));
application.registerShutdownHook();
}
输出结果:
上下文刷新事件:com.run.test.event.ContextRefreshedEventListener
收到:com.run.context.support.ClassPathXmlApplicationContext@369f73a2消息,当前时间:Sun Apr 02 10:59:07 CST 2023
消息:20000001:我是自定义的事件
上下文关闭事件:com.run.test.event.ContextClosedEventListener
Process finished with exit code 0
可以看到,我们自己定义的事件和监听,以及监听系统的事件信息,都完美的触发了。
主要流程:
用户自定义一个事件,继承自 ApplicationEvent,通过构造函数将该事件源对象 source 注入进去,以及填充一些自定义的参数;
自定义事件后,需要定义一个针对该事件的监听器,需要实现 ApplicationListener 接口,用于处理一些自定义的操作。这些操作通过实现 ApplicationListener#onApplicationEvent() 方法,在该方法中自定义的操作;
定义好事件和对应的监听器后,就可以在指定的地方通过上下文 ApplicationContext 的 publishEvent() 方法发布事件了,发布后的执行流程如下: