目录
一、观察者模式
1.1 模型介绍
1.2 观察者模式Demo
1.2.1 观察者实体
1.2.2 主题实体
1.2.3 测试代码
二、Spring监听器的介绍
2.1 事件(ApplicationEvent)
2.1.1 Spring内置事件
2.1.2 Spring内置事件
2.2 事件监听器(ApplicationListener)
2.2.1 事件监听器-基于接口
2.2.1.1 SmartApplicationListener
2.2.1.2 GenericApplicationListener
2.2.2 事件监听器-基于注解
2.3 事件发布者(ApplicationEventPublisher)
2.4 事件广播器(ApplicationEventMulticaster)
三、Spring监听器的使用
3.1 新建监听器
3.1.1 通过实现ApplicationListener接口创建监听器
3.1.2 使用@EventListener注解创建监听器
3.2 内置的事件类型
3.3 自定义事件与监听器Demo
3.3.1 构建两个自定义事件
3.3.2 构建监听
3.3.3 发布事件
Spring的监听器用到了观察者模式,工厂模式(EventListenerFactory),适配器模式(ApplicationListenerMethodAdapter),而其中最重要的就是基于观察者模式,它能实现事件与事件监听者直接的解耦。所以在讲解监听器之前,我们先讲一下观察者模式。
观察者模式(Observer Pattern)是一种行为设计模式,它用于在对象之间建立一对多的依赖关系。在该模式中,当一个对象的状态发生变化时,它会自动通知其依赖对象(称为观察者),使它们能够自动更新。
观察者模式的工作原理如下:
以我的理解,就是一个主题会去监听一种事件,和这个事件相关的观察者都会被加到这个主题对象中。当主题对象监听到这个事件发生了,就会去通知自己维护的所有观察者,这些观察者就回去执行相应的逻辑。也就是一种主题对应一种事件,要观察这个事件的观察者都添加到这个主题中,由主题去监听这个事件是否发生,事件发生后主题回去通知所有的观察者,观察者们再去执行相应的逻辑。
观察者模式角色:
一个观察者模式demo包括以下部分:
所以我们先写个观察者接口
interface Observer {
void update();
}
再构建两个观察者实现类
// 具体观察者A
class ConcreteObserverA implements Observer {
@Override
public void update() {
System.out.println("ConcreteObserverA收到更新通知");
}
}
// 具体观察者B
class ConcreteObserverB implements Observer {
@Override
public void update() {
System.out.println("ConcreteObserverB收到更新通知");
}
}
然后定义主题接口
interface Subject {
// 注册新的观察者
void registerObserver(Observer observer);
// 删除指定观察者
void removeObserver(Observer observer);
// 通知全部观察者
void notifyObservers();
}
构建一个具体主题
// 具体主题
class ConcreteSubject implements Subject {
// 一个主题维护多个观察者
private List observers = new ArrayList<>();
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update();
}
}
public void doSomething() {
System.out.println("主题执行某些操作...");
// 执行操作后通知观察者
notifyObservers();
}
}
public class ObserverPatternDemo {
public static void main(String[] args) {
// 创建主题和观察者
ConcreteSubject subject = new ConcreteSubject();
Observer observerA = new ConcreteObserverA();
Observer observerB = new ConcreteObserverB();
// 注册观察者
subject.registerObserver(observerA);
subject.registerObserver(observerB);
// 执行主题的操作,触发通知
subject.doSomething();
}
}
最后可以看到结果:
主题执行某些操作…
ConcreteObserverA收到更新通知
ConcreteObserverB收到更新通知
Spring事件体系包括三个组件:
实现Spring事件机制主要有4个类:
事件(ApplicationEvent) 是特定事件监听器被触发的原因。
内置事件中由系统内部进行发布,只需自己注入监听器即可。
Spring容器启动的时候在不同阶段会发布不同事件,我们可以根据自己的需求进行监听。(阿里的nacos服务自动注册原理就是监听到web容器初始化完成事件,完成自动注册发现)。例如Spring容器启动完之后,会发布一个ContextRefreshedEvent事件,我们可以实现自己的监听器进行监听。
@Component
// 设置用于监听ContextRefreshedEvent事件的监听器
public class MyListener2 implements ApplicationListener {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
System.out.println("-----------------:"+event.getSource().getClass());
}
}
可以使用Spring内置的事件来对注册到容器中的Bean进行拓展,重点看内置的ContextRefreshedEvent和ContextClosedEvent这两个事件,一个是在刚完成容器初始化的时候被发布的事件,一个是关闭容器时发布的事件。我们只要去编写监听这两个事件的监听器,来自定义实现一些逻辑,就能做到对注册到容器中的Bean进行拓展了。
内置的事件类型:
Event |
说明 |
ContextRefreshedEvent |
当容器被实例化或refreshed时发布。如调用refresh()方法,此处的实例化是指所有的bean都已被加载,后置处理器都被激活,所有单例bean都已被实例化,所有的容器对象都已准备好可使用。如果容器支持热重载,则refresh可以被触发多次(XmlWebApplicatonContext支持热刷新,而 GenericApplicationContext则不支持) |
ContextStartedEvent |
当容器启动时发布,即调用start()方法,已启用意味着所有的Lifecycle bean都已显式接收到了start 信号 |
ContextStoppedEvent |
当容器停止时发布,即调用stop()方法,即所有的Lifecycle bean都已显式接收到了stop信号,关闭的容器可以通过start()方法重启 |
ContextClosedEven36 |
当容器关闭时发布,即调用close方法,关闭意味着所有的单例bean都已被销毁。关闭的容器不能被重启 或refresh |
RequestHandledEvent |
这只在使用spring的DispatcherServlet时有效,当一个请求被处理完成时发布 |
内置事件类型的继承体系:
事件类需要继承ApplicationEvent抽象类,代码如下:
public class BigEvent extends ApplicationEvent {
private String name;
public BigEvent(Object source, String name) {
super(source);
this.name = name;
}
public String getName() {
return name;
}
}
这里为了简单测试,所以写的很简单。
事件类其实就是一种很简单的pojo,除了需要继承ApplicationEvent也没什么了,这个类有一个构造方法需要调用super()方法。
事件监听器(ApplicationListener)对应于观察者模式中的观察者。作用为处理事件广播器发布的事件。
在Spring中监听器的实现主要有一下重要组件:
Spring提供了继承于java.util.EventListener接口的应用监听器接口, ApplicationListener,此接口源码:
@FunctionalInterface
public interface ApplicationListener extends EventListener {
/**
* Handle an application event.
* @param event the event to respond to
*/
void onApplicationEvent(E event);
}
使用接口实现监听器:
@FunctionalInterface
public interface ApplicationListener extends EventListener {
/**
* Handle an application event.
* @param event the event to respond to
*/
void onApplicationEvent(E event);
}
事件监听器需要实现ApplicationListener接口,这是个泛型接口,泛型类类型就是事件类型,其次监听器是需要spring容器托管的bean,所以这里加了@component,只有一个方法,就是onApplicationEvent(),是事件发生后要触发的操作。
Spring还提供了对ApplicationListener接口的实现:SmartApplicationListener接口和GenericApplicationListener接口。
提供了监听器对泛型事件的支持,spring3.0 添加的。
源码:
public interface SmartApplicationListener extends ApplicationListener, Ordered {
boolean supportsEventType(Class extends ApplicationEvent> var1);
boolean supportsSourceType(Class> var1);
}
增强对泛型事件的支持(支持泛型方式不同与SmartApplicationListener),spring4.2 添加的。
源码:
public interface GenericApplicationListener extends ApplicationListener, Ordered {
boolean supportsEventType(ResolvableType var1);
boolean supportsSourceType(Class> var1);
}
@Component
public class OrderEventListener {
@EventListener(OrderEvent.class)
public void onApplicationEvent(OrderEvent event) {
if(event.getName().equals("减库存")){
System.out.println("减库存.......");
}
}
}
注解是加到onApplicationEvent()方法上的(方法名可以自定义,这个方法就是监听器监听到事件发生后触发的执行逻辑)。
这里要注意,加注解的方法不能是private修饰的方法,否则会报错。
@EventListener的错误尝试:
道了@EventListener的原理,我们其实可以做一些猜测,如下:
methodA是正常的用法;
methodB方法的修饰符是private;
methodC则是监听的ContextRefreshedEvent,但下面方法的入参却是ContextClosedEvent;
后两者都有问题:
可以看到,编译器直接黄底提示了methodB的@EventListener注解,其实从前面我们已经猜到,因为最后我们的调用是由监听器ApplicationListenerMethodAdapter对象直接调用的方法ABC,所以方法必须可被其他对象调用,即public。
而后者会在执行广播响应事件时报参数非法异常也是意料之中。
事件发布者,职责为发布事件。
spring的ApplicationContext 本来就实现了ApplicationEventPublisher接口,因此应用上下文本来就是
一个事件发布者,在AbstractApplicationContext中实现了事件发布的业务。
上下文对象调用publishEvent()方法,将事件对象传入即可将该事件发布。
applicationContext.publishEvent(new HelloEvent(this,"lgb"));
事件发布之后就会被已经注册到Spring容器中对应的事件监听器监听到,会触发监听器onApplicationEvent()操作。
Spring事件机制是观察者模式的一种实现,但是除了发布者和监听者者两个角色之外,还有一个EventMultiCaster的角色负责把事件转发给监听者。
事件发布器(ApplicationEventMulticaster)又叫事件发布器、事件多播器。对应于观察者模式中的被观察者/主题, 负责通知观察者。对外提供发布事件和增删事件监听器的接口,维护事件和事件监听器之间的映射关系,并在事件发生时负责通知相关监听器。
其工作流程如下:
EventMultiCaster注册着所有的Listener,在上面发布事件的代码中发布者调用applicationEventPublisher.publishEvent(msg); 是会将事件发送给了EventMultiCaster,然后EventMultiCaster根据事件类型决定转发给那个Listener。
讲完了Spring事件体系中的三个组件,我们就明白了他们之间的关系和运作流程。下面我们再详细讲一下如何使用它。
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
@Component
public class MyContextRefreshedListener implements ApplicationListener {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
System.out.println("应用程序上下文已刷新");
// 在这里可以执行一些初始化操作
}
}
除了手动写个类外,我们也可以找个现成的类,该类不需要继承或实现任何其他类,只需要在它的某个方法上加上 @EventListener 注解就可以实现监听器的功能,如下:
@Component
public class MyListener {
@EventListener(ContextRefreshedEvent.class)
public void methodA(ContextRefreshedEvent event) {
System.out.println("应用程序上下文已刷新");
// 在这里可以执行一些初始化操作
}
}
上述两种方法的效果是一样的。那么最后,我们就完成了Spring中一个内置监听器的简单示例:当启动一个基于Spring的应用程序时,当应用程序上下文被刷新时,ContextRefreshedEvent事件将被触发,然后MyContextRefreshedListener监听器的onApplicationEvent方法将被调用。
我们在demo中使用了一个 ContextRefreshedEvent 的事件,这个事件是Spring内置的事件,除了该事件,Spring还内置了一些其他的事件类型,分别在以下情况下触发:
当应用程序上下文被刷新时触发。这个事件在ApplicationContext初始化或刷新时被发布,适用于执行初始化操作和启动后的后续处理。例如,初始化缓存、预加载数据等。
当应用程序上下文启动时触发。这个事件在调用ApplicationContext的start()方法时被发布,适用于在应用程序启动时执行特定的操作。例如,启动定时任务、启动异步消息处理等。
当应用程序上下文停止时触发。这个事件在调用ApplicationContext的stop()方法时被发布,适用于在应用程序停止时执行清理操作。例如,停止定时任务、关闭数据库连接等。
当应用程序上下文关闭时触发。这个事件在调用ApplicationContext的close()方法时被发布,适用于在应用程序关闭前执行最后的清理工作。例如,释放资源、保存日志等。
在Web应用程序中,当一个HTTP请求处理完成后触发。这个事件在Spring的DispatcherServlet处理完请求后被发布,适用于记录请求日志、处理统计数据等。
这是一个抽象的基类,可以用于定义自定义的应用程序事件。你可以创建自定义事件类,继承自ApplicationEvent,并定义适合你的应用场景的事件类型。
这些内置监听器是由Spring框架自动发布的。不需要程序员去发布。
在学习完上面的内容后,我们现在可以手动写个Spring的事件,以及对应的监听器的demo了
Spring通过继承ApplicationEvent抽象类来创建自定义事件。下面我们来建立一个继承自ApplicationEvent的自定义事件类。
// 事件A
public class CustomEventA extends ApplicationEvent {
private String message;
public CustomEventA(Object source, String message) {
super(source);
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
// 事件B
public class CustomEventB extends ApplicationEvent {
private String message;
public CustomEventB(Object source, String message) {
super(source);
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
我们选用@EventListener注解来实现监听器。
@Component
public class MyListener {
@EventListener(CustomEventA.class)
public void methodA(CustomEventA event) {
System.out.println("========我监听到事件A了:" + event.getMessage());
// 在这里可以执行一些其他操作
}
@EventListener(CustomEventB.class)
public void methodB(CustomEventB event) {
System.out.println("========我监听到事件B了:" + event.getMessage());
// 在这里可以执行一些其他操作
}
}
@Component
// 这里将这个CustomEvent事件发布器也设置成一个事件监听器了,去监听ContextRefreshedEvent事件,当这个时间发生之后就会自动去发布CustomEvent事件
public class CustomEventPublisher implements ApplicationContextAware, ApplicationListener {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
// 利用容器刷新好的消息作为触发,去发布两条自定义的事件
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// 创建事件
CustomEventA eventA = new CustomEventA(applicationContext , "我是AAAA");
CustomEventB eventB = new CustomEventB(applicationContext , "我是BBBB");
// 发布事件
applicationContext.publishEvent(eventA);
applicationContext.publishEvent(eventB);
}
}