文章目录
- 目标
- 设计
- 项目结构
- 一、实现
- 1、定义事件接口--ApplicationEvent
- 1、定义通用应用上下文事件--ApplicationContextEvent
- 2、定义刷新和关闭事件--ContextClosedEvent、ContextRefreshedEvent
- 2、定义事件监听--ApplicationListener
- 3、定义事件广播器--ApplicationEventMulticaster
- 1、抽象实现事件广播器--AbstractApplicationEventMulticaster
- 2、实现事件的广播功能--SimpleApplicationEventMulticaster
- 3、定义事件发布--ApplicationEventPublisher
- 1、抽象实现事件发布实现--AbstractApplicationContext
- 二、测试
- 1、创建一个自定义事件和监听器
- 2、创建刷新事件监听
- 3、创建关闭事件监听
- 4、配置文件
- 5、单元测试
- 总结
目标
在 Spring 中有一个 Event 事件功能,它可以提供事件的定义、发布以及监听事件来完成一些自定义的动作,可以降低系统的耦合
如提交某个申请,可能需要发MQ短信通知审批人员去审批,如启动审批流程等
本章节我们需要以观察者模式的方式,设计和实现 Spring Event 的容器事件和事件监听器功能,最终可以让我们在现有实现的 Spring 框架中可以定义、监听和发布自己的事件信息。
设计
在功能实现上我们需要定义出事件类、事件监听、事件发布,而这些类的功能需要结合到 Spring 的 AbstractApplicationContext#refresh(),以便于处理事件初始化和注册事件监听器的操作。整体设计结构如下图
橙色是本章新增内容
1、在整个功能实现过程中,仍然需要在面向用户的应用上下文 AbstractApplicationContext 中添加相关事件内容,包括:初始化事件发布者、注册事件监听器、发布容器刷新完成事件。
2、使用观察者模式定义事件类、监听类、发布类,同时还需要完成一个广播器的功能,接收到事件推送时进行分析处理符合监听事件接受者感兴趣的事件,也就是使用 isAssignableFrom 进行判断。
3、isAssignableFrom 和 instanceof 相似,不过 isAssignableFrom 是用来判断子类和父类的关系的,或者接口的实现类和接口的关系的,默认所有的类的终极父类都是Object。如果A.isAssignableFrom(B)结果是true,证明B可以转换成为A,也就是A可以由B转换而来。
项目结构
本章新增、修改的类,可以看到我们在现有的spring代码结构上,增加了事件的定义、事件监听、事件广播、事件发布等功能,对原来的代码改动很少,仅仅是在ApplicationContext中,继承了ApplicationEventPublisher,由AbstractApplicationContext具体实现了事件发布(广播事件),这全部得益于前期的架构设计,才能像现在这样,即使添加新的功能,对原来的代码改动也非常小,更具备扩展性。
1、以上整个类关系图以围绕实现 event 事件定义、发布、监听功能实现和把事件的相关内容使用 AbstractApplicationContext#refresh 进行注册和处理操作。
2、在实现的过程中主要以扩展 spring context 包为主,事件的实现也是在这个包下进行扩展的,当然也可以看出来目前所有的实现内容,仍然是以IOC为主。
3、ApplicationContext 容器继承事件发布功能接口 ApplicationEventPublisher,并在实现类中提供事件监听功能。
4、ApplicationEventMulticaster 接口是注册监听器和发布事件的广播器,提供添加、移除和发布事件方法。
5、最后是发布容器关闭事件,这个仍然需要扩展到 AbstractApplicationContext#close 方法中,由注册到虚拟机的钩子实现
一、实现
1、定义事件接口–ApplicationEvent
public abstract class ApplicationEvent extends EventObject {
/**
* @desc: 创建事件
**/
public ApplicationEvent(Object source) {
super(source);
}
}
以继承 java.util.EventObject 定义出具备事件功能的抽象类 ApplicationEvent,后续所有事件的类都需要继承这个类。
1、定义通用应用上下文事件–ApplicationContextEvent
public class ApplicationContextEvent extends ApplicationEvent {
/**
* @desc: 创建事件
**/
public ApplicationContextEvent(Object source) {
super(source);
}
/**
* @desc: 获取事件引发的应用上下文
**/
public final ApplicationContext getApplicationContext(){
return (ApplicationContext) getSource();
}
}
ApplicationContextEvent 是定义事件的抽象类,所有的事件包括关闭、刷新,以及用户自己实现的事件,都需要继承这个类。
2、定义刷新和关闭事件–ContextClosedEvent、ContextRefreshedEvent
/**
* @desc 定义关闭事件
*/
public class ContextClosedEvent extends ApplicationContextEvent {
/**
* @desc: 创建事件
**/
public ContextClosedEvent(Object source) {
super(source);
}
}
/**
* @desc 定义刷新事件
*/
public class ContextRefreshedEvent extends ApplicationContextEvent {
/**
* @desc: 创建事件
**/
public ContextRefreshedEvent(Object source) {
super(source);
}
}
1、ContextClosedEvent、ContextRefreshedEvent,分别是 Spring 框架自己实现的两个事件类,可以用于监听刷新和关闭动作。
2、定义事件监听–ApplicationListener
/** 事件监听器
*/
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
/**
* 处理事件
*/
void onApplicationEvent(E event);
}
定义了一个处理事件,这个接口由使用者实现,添加自定义处理事件的逻辑
程序员想使用,一般需要实现ApplicationListener接口,并且通过指定泛型,指定事件类,如implements ApplicationListener,然后实现onApplicationEvent的实现
3、定义事件广播器–ApplicationEventMulticaster
/**
* @desc 事件广播器
*/
public interface ApplicationEventMulticaster {
/**
* 添加监听器
**/
void addApplicationListener(ApplicationListener<?> listener);
/**
* 删除监听器
*/
void removeApplicationListener(ApplicationListener<?> listener);
/**
* 将事件广播
*/
void multicastEvent(ApplicationEvent event);
}
在事件广播器中定义了添加监听和删除监听的方法以及一个广播事件的方法(multicastEvent), 最终推送事件消息也会经过这个接口方法来处理谁该接收事件。
1、抽象实现事件广播器–AbstractApplicationEventMulticaster
/**
* @desc 抽象事件广播器
*/
public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster, BeanFactoryAware {
private BeanFactory beanFactory;
private final Set<ApplicationListener<ApplicationEvent>> applicationListeners = new LinkedHashSet<>();
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override
public void addApplicationListener(ApplicationListener<?> listener) {
applicationListeners.add((ApplicationListener<ApplicationEvent>) listener);
}
@Override
public void removeApplicationListener(ApplicationListener<?> listener) {
applicationListeners.remove(listener);
}
/**
* @desc: 获取感兴趣事件的监听
**/
protected Collection<ApplicationListener> getApplicationListeners(ApplicationEvent event) {
LinkedHashSet<ApplicationListener> allListeners = new LinkedHashSet<>();
for (ApplicationListener<ApplicationEvent> applicationListener : applicationListeners) {
if (supportsEvent(applicationListener,event)) {
allListeners.add(applicationListener);
}
}
return allListeners;
}
/**
* @desc:监听器是否对该事件感兴趣
**/
protected boolean supportsEvent(ApplicationListener<ApplicationEvent> applicationListener, ApplicationEvent event) {
Class<? extends ApplicationListener> listenerClass = applicationListener.getClass();
// 按照 CglibSubclassingInstantiationStrategy、SimpleInstantiationStrategy 不同的实例化类型,需要判断后获取目标 class
Class<?> targetClass = ClassUtils.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);
}
// 判定此 eventClassName 对象所表示的类或接口与指定的 event.getClass() 参数所表示的类或接口是否相同,或是否是其超类或超接口。
// isAssignableFrom是用来判断子类和父类的关系的,或者接口的实现类和接口的关系的,默认所有的类的终极父类都是Object。如果A.isAssignableFrom(B)结果是true,证明B可以转换成为A,也就是A可以由B转换而来。
return eventClassName.isAssignableFrom(event.getClass());
}
}
1、AbstractApplicationEventMulticaster 是对事件广播器的公用方法提取,在这个类中可以实现一些基本功能,避免所有直接实现接口放还需要处理细节。
2、除了像 addApplicationListener、removeApplicationListener,这样的通用方法,这里这个类中主要是对 getApplicationListeners 和 supportsEvent 的处理。
3、getApplicationListeners 方法主要是摘取符合广播事件中的监听处理器,具体过滤动作在 supportsEvent 方法中。
4、在 supportsEvent 方法中,主要包括对Cglib、Simple不同实例化需要获取目标Class,Cglib代理类需要获取父类的Class,普通实例化的不需要。
5、接下来就是通过提取接口和对应的 ParameterizedType 和 eventClassName,方便最后确认是否为子类和父类的关系,以此证明此事件归这个符合的类处理。可以参考代码中的注释
2、实现事件的广播功能–SimpleApplicationEventMulticaster
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster{
public SimpleApplicationEventMulticaster(BeanFactory beanFactory) {
setBeanFactory(beanFactory);
}
@Override
public void multicastEvent(ApplicationEvent event) {
for (ApplicationListener applicationListener : getApplicationListeners(event)) {
applicationListener.onApplicationEvent(event);
}
}
}
1、继承AbstractApplicationEventMulticaster,实现了multicastEvent(广播接口)
2.、multicastEvent获取到符合事件的监听集合,
3、定义事件发布–ApplicationEventPublisher
/**
* @desc 事件发布者
*/
public interface ApplicationEventPublisher {
/**
* @desc: 发布事件
**/
void publishEvent(ApplicationEvent event);
}
ApplicationEventPublisher 是整个一个事件的发布接口,所有的事件都需要从这个接口发布出去。
1、抽象实现事件发布实现–AbstractApplicationContext
/**
* 应用上下文抽象类实现
*/
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
private ApplicationEventMulticaster applicationEventMulticaster;
/**
* @desc: 刷新容器
**/
@Override
public void refresh() throws BeansException {
// 1、创建BeanFactory,并加载BeanDefintion
refreshBeanFactory();
// 2、获取beanFactory
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// 3. 添加 ApplicationContextAwareProcessor,让继承自 ApplicationContextAware 的 Bean 对象都能感知所属的 ApplicationContext
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
// 4.在 Bean 实例化之前,执行 BeanFactoryPostProcesso
invokeBeanFactoryPostProcessors(beanFactory);
// 5、BeanPostProcessor 需要提前于其他 Bean 对象实例化之前执行注册操作
registerBeanPostProcessors(beanFactory);
// 6. 初始化事件发布者
initApplicationEventMulticaster();
// 7. 注册事件监听器
registerListeners();
// 8. 提前实例化单例Bean对象
beanFactory.preInstantiateSingletons();
// 9. 发布容器刷新完成事件
finishRefresh();
}
/**
* @desc: 初始化事件广播器
**/
private void initApplicationEventMulticaster(){
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME,applicationEventMulticaster);
}
/**
* @desc: 注册程序员定义的各种事件监听器
**/
private void registerListeners() {
Collection<ApplicationListener> applicationListeners = getBeansOfType(ApplicationListener.class).values();
for (ApplicationListener listener : applicationListeners) {
applicationEventMulticaster.addApplicationListener(listener);
}
}
/**
* @desc: 发布容器刷新完成事件
**/
private void finishRefresh() {
publishEvent(new ContextRefreshedEvent(this));
}
/**
* @desc: 事件广播器,广播事件
**/
@Override
public void publishEvent(ApplicationEvent event) {
applicationEventMulticaster.multicastEvent(event);
}
/**
* 添加钩子方法close
*/
@Override
public void registerShutdownHook() {
Runtime.getRuntime().addShutdownHook(new Thread(this::close));
}
/**
* 调用关闭方法时,会执行销毁的自定义处理
*/
@Override
public void close() {
publishEvent(new ContextClosedEvent(this));
getBeanFactory().destroySingletons();
}
// ..屏蔽掉getbean、beanPostProcesssor等本章无关代码
}
1、在抽象应用上下文 AbstractApplicationContext#refresh 中,主要新增了 初始化事件发布者、注册事件监听器、发布容器刷新完成事件,三个方法用于处理事件操作。
2、初始化事件发布者(initApplicationEventMulticaster),主要用于实例化一个 SimpleApplicationEventMulticaster,这是一个实现的事件广播器。
3、注册事件监听器(registerListeners),通过 getBeansOfType 方法获取到所有从 spring.xml 中加载到的事件配置 Bean 对象。
4、发布容器刷新完成事件(finishRefresh),发布了第一个服务器启动完成后的事件,这个事件通过 publishEvent 发布出去,其实也就是调用了 applicationEventMulticaster.multicastEvent(event); 方法。
5、最后是一个 close 方法中,新增加了发布一个容器关闭事件。publishEvent(new ContextClosedEvent(this));
二、测试
1、创建一个自定义事件和监听器
public class CustomEvent extends ApplicationContextEvent {
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;
}
// ...get/set
}
创建一个自定义事件,在事件类的构造函数中可以添加自己的想要的入参信息。这个事件类最终会被完成的拿到监听里,所以你添加的属性都会被获得到。
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());
}
}
1、这个是一个用于监听 CustomEvent 事件的监听器,这里你可以处理自己想要的操作,比如一些用户注册后发送优惠券和短信通知等。
2、创建刷新事件监听
/**
* @desc: 刷新监听
**/
public class ContextRefreshedEventListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
System.out.println("刷新事件:" + this.getClass().getName());
}
}
因为spring框架涵本身就提供了ContextRefreshedEvent事件,所以这里无需和前面步骤1一样,自己去定义一个刷新事件,所以这里只需要创建关闭的监听,然后自定义处理逻辑
3、创建关闭事件监听
/**
* @desc: 关闭监听器
**/
public class ContextClosedEventListener implements ApplicationListener<ContextClosedEvent> {
@Override
public void onApplicationEvent(ContextClosedEvent event) {
System.out.println("关闭事件:" + this.getClass().getName());
}
}
同步骤2一致,当然实际需求中,可以根据自己的需求创建需要的事件和监听
4、配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean class="springframework.test.event.ContextRefreshedEventListener"/>
<bean class="springframework.test.event.CustomEventListener"/>
<bean class="springframework.test.event.ContextClosedEventListener"/>
</beans>
在 spring.xml 中配置了三个事件监听器,监听刷新、监控自定义事件、监听关闭事件。
5、单元测试
public class ApiTest {
@Test
public void test_event() {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
applicationContext.publishEvent(new CustomEvent(applicationContext, 1019129009086763L, "成功了!"));
applicationContext.registerShutdownHook();
}
}
通过使用 applicationContext 新增加的发布事件接口方法,发布一个自定义事件 CustomEvent,并透传了相应的参数信息。
测试结果
刷新事件:springframework.test.event.ContextRefreshedEventListener$$EnhancerByCGLIB$$828882b7
收到:cn.ljc.springframework.context.support.ClassPathXmlApplicationContext@50f8360d消息;时间:Tue Dec 20 23:50:44 CST 2022
消息:1019129009086763:成功了!
关闭事件:springframework.test.event.ContextClosedEventListener$$EnhancerByCGLIB$$9654bf79
Process finished with exit code 0
从测试结果可以看到,我们自己定义的事件和监听,以及监听系统的事件信息,都可以在控制台完整的输出出来了。
你也可以尝试增加一些其他事件行为,并调试代码学习观察者模式。
总结
1、spring的事件定义、定义监听、事件发布,其实就是设计模式的具体实现
2、在发布完成后根据匹配策略,监听器就会收到属于自己的事件内容,并做相应的处理动作
3、不熟悉的地方还是需要一点点调试,并且结合源码才能慢慢掌握