观察者模式实现事件监听机制

Spring 为什么需要事件,可以用来干什么?

用来解耦代码。高内聚,低耦合的设计思想

可以用于处理日志监控和各种事件发布订阅的客制化定义

image-20230211141540391

观察者模式

在开发中,有很多利用到解耦的场景,例如在注册事件完成后异步发送事件推送消息,用户下单后异步发送MQ消息给与用户后续业务的跟进情况,用户付完款后就可以发送货物派送进度,都是依靠事件订阅和发布以及MQ消息这样的组件,来处理系统之间的调用解耦,最终也通过解耦的方式来提升整体系统架构的稳定性和负载能力

其实解耦思想就是观察者模式的一种体现。当对象间存在一对多关系时,则适合使用观察者模式,它定义了对象之间的一种一对多依赖关系。当一个对象的状态改变时,所有依赖他,与他有关的对象都会得到通知并且自动更新

观察者模式角色

Subject:抽象主题(抽象被观察者),抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。

ConcreteSubject:具体主题(具体被观察者),该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。

Observer:抽象观察者,是观察者者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。

ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。

image-20230211133858911

这一节中和前几节结合起来,才更能明白 Spring 框架,在 Spring 框架中,我感觉是万物即 Bean 对象

就像本节的事件监听器,就是通过 xml 文件配置,被 ClassPathXmlApplicationContext 面向用户的应用上下文读取出来并通过 bean 对象加载。在上一节中 FactoryBean 实际上也是通过 xml 配置,读取出来实例化成 bean。只不过这个 bean 我们创建的更加简单,不需要配置引用类型。

设计

手写Spring-事件发布和监听机制

事件

在观察者模式中,我们提到一个事件想要运作,需要 事件、事件广播器(事件监听器)、事件发布器 三大部分,事件广播器实际上是一个管理 事件监听器的容器,负责处理其添加、触发。在 AbstractApplicationContext 中,initApplicationEventMulticasterregisterListeners去实现事件监听器的注册添加,因为 Spring IoC 容器中万物皆 Bean,因此监听器是由单例模式注册的,之后将所有的监听器都交由广播器来统一管理。

private void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
// 将监听器注册为单例 bean 对象,在 Spring 里,所有对象都使用 bean 管理
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BASE_NAME, applicationEventMulticaster);
}

/**
* 注册事件监听器
*/
private void registerListeners() {
// 通过 getBeansOfType 方法获取道所有在 spring.xml 中加载到的事件配置 Bean 对象
Collection<ApplicationListener> applicationListeners = getBeansOfType(ApplicationListener.class).values();
for (ApplicationListener listener : applicationListeners) {
applicationEventMulticaster.addApplicationLister(listener);
}
}

事件机制的行为如下:

image-20230213173412299

同时还有专门处理容器关闭和容器刷新的监听器 ContextCloseEventListenerContextRefreshEventListener

并且由于是事件机制,它也是异步的,当等到一个事件的发布,事件监听器才会被触发去执行相应逻辑,即调用onApplicationEvent方法完成相关逻辑

image-20230213174555458

具体情况如图,事件广播器接受到事件发布之后,调用内部广播方法,遍历通知所有被管理的监听器,监听器调用各自的执行方法。完成事件订阅与接收逻辑

工程

约定接口(整个事件机制的基石)

监听器接口

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

/**
* 监听器在接受到发布消息后各自需要完成的逻辑
* @param event the event to respond to
*/
void onApplicationEvent(E event);

}

发布器接口

public interface ApplicationEventPublisher {

/**
* 通知在此应用程序中注册的所有侦听器应用程序事件。
* 事件可以是框架事件(如 RequestHandledEvent)或特定于应用程序的事件。
* @param event 发布事件
*/
void publishEvent(ApplicationEvent event);

}
  • 只处理发布事件的逻辑

事件广播器

/**
* 事件广播器(管理所有事件监听器)
* 消息 --> 事件发布者推送 --> 事件广播器接收 --> 通知所有事件监听器接收
* @author BanTanger 半糖
* @Date 2023/2/11 15:26
*/
public interface ApplicationEventMulticaster {

/**
* 添加事件监听器
* @param listener
*/
void addApplicationLister(ApplicationListener<?> listener);

/**
* 移除事件监听器
* @param listener
*/
void removeApplicationListener(ApplicationListener<?> listener);

/**
* 广播事件
* 最终推送时间消息也会经过这个接口方法来处理谁该接收事件。
* @param event
*/
void multicastEvent(ApplicationEvent event);

}

自定义事件的实现

  • 数据结构为 Event{ id, message }

事件监听器(刷新容器、关闭容器、自定义事件监听)的实现逻辑

总结

AbstractApplicationContext 中集成了发布事件以及添加事件监听器的逻辑,并且提供了接口,这就是我们事件的入口

如何获取这个入口

  1. 像测试用例一样,直接用xml去获取ClassPathXmlApplicationContext
  2. 通过ApplicationContextAware

ApplicationContextAware

在 AbstractApplicationContext#refresh,会为实现了 ApplicationContextAware 的类设置好对应的 ApplicationContext,那就可以通过这样获取到入口。

而这个操作的实现,是注入了一个实现了BeanPostProcessor的专门用于添加ApplicationContext的实例ApplicationContextAwareProcessor

那么由此又可以引申出,对象创建,BeanFactoryPostProcessor,ApplicationContext,Bean的生命周期,毕竟这 些都是在Bean的生命周期里实现的。

通过这章又可以回想起Cglib方式创建的对象,那是源于当时最初没有对有参构造进行特殊设置,从而有cglib和普通的反射方法创建的不同策略。

spring的IOC所进行的就是对Bean进行管理,Bean就是一个个的实例化对象(Spring IoC 中万物即 Bean 对象。每个Bean从创建到销毁,中间他是有许许多多的过程,它就使用了 ApplicationContext 进而扩展,对每个环节进行控制和扩展方法。比如对象创建前后的操作,对象感知容器,复杂对象的创建可以用另一种 FactoryBean 的方法,对事件的控制等等