前言
相信大家都或多或少知道Spring中的监听器,有些人还能说出它采用了观察者模式,但其实它还用到了适配器模式,工厂模式等。当然,仍有不少人是完全不了解Spring的监听及其机制的,本次我们就来深入学习一下Spring监听器
一、Spring监听器是什么
Spring监听器是一种特殊的类,它们能帮助开发者监听 web 中特定的事件,比如 ServletContext, HttpSession, ServletRequest 的创建和销毁;变量的创建、销毁等等。
当Web容器启动后,Spring的监听器会启动监听,监听是否创建ServletContext的对象,如果发生了创建ServletContext对象这个事件 (当web容器启动后一定会生成一个ServletContext对象,所以监听事件一定会发生),ContextLoaderListener类会实例化并且执行初始化方法,将spring的配置文件中配置的bean注册到Spring容器中
二、观察者模式
1. 模型介绍
观察者模式(Observer Pattern)是一种行为设计模式,它用于在对象之间建立一对多的依赖关系。在该模式中,当一个对象的状态发生变化时,它会自动通知其依赖对象(称为观察者),使它们能够自动更新
观察者模式的工作原理如下:
- 主题对象维护一个观察者列表,并提供方法用于添加和删除观察者。
- 当主题的状态发生变化时,它会遍历观察者列表,并调用每个观察者的通知方法。
- 观察者接收到通知后,根据通知进行相应的更新操作。
2. 观察者模式Demo
一个观察者模式demo包括以下部分:
观察者实体
主题实体
所以我们先写个观察者接口:
import java.util.ArrayList; import java.util.List; 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<Observer> 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监听器应用
1. 新建监听器
1.1 实现ApplicationListener接口
在详细介绍Spring监听器前,我们以一个简单的demo来说明:
import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.stereotype.Component; @Component public class MyContextRefreshedListener implements ApplicationListener<ContextRefreshedEvent> { @Override public void onApplicationEvent(ContextRefreshedEvent event) { System.out.println("应用程序上下文已刷新"); // 在这里可以执行一些初始化操作 } }
- 我们创建了一个名为MyContextRefreshedListener* 的监听器,它实现了ApplicationListener接口,这意味着这个监听器监听的事件类型是ContextRefreshedEvent,并重写了 onApplicationEvent 方法。该方法在应用程序上下文被刷新时触发。
- 使用 @Component 注解将该监听器声明为一个Spring管理的组件,这样Spring会自动将其纳入到应用程序上下文中,并在适当的时候触发监听
1.2 使用@EventListener注解
除了手动写个类外,我们也可以找个现成的类,该类不需要继承或实现任何其他类,然后在它的某个方法上加上 @EventListener 注解,如下:
@Component public class MyListener { @EventListener(ContextRefreshedEvent.class) public void methodA(ContextRefreshedEvent event) { System.out.println("应用程序上下文已刷新"); // 在这里可以执行一些初始化操作 } }
- 在一个现有的类的某个方法上,加上@EventListener(ContextRefreshedEvent.class),Spring会在加载这个类时,为其创建一个监听器,这个监听器监听的事件类型是ContextRefreshedEvent,当此事件发生时,将触发执行该方法methodA。
- 使用 @Component 注解将该类声明为一个Spring管理的组件,这样Spring会自动将其纳入到应用程序上下文中,并在适当的时候触发监听
- 我们可以在这个类中写上多个方法,每个方法通过注解监听着不同的事件类型,这样我们就仅需使用一个类,却构建了多个监听器
上述两种方法的效果是一样的。那么最后,我们就完成了Spring中一个内置监听器的简单示例:当启动一个基于Spring的应用程序时,当应用程序上下文被刷新时,ContextRefreshedEvent事件将被触发,然后MyContextRefreshedListener监听器的onApplicationEvent方法将被调用。
2. 内置的事件类型
我们在demo中使用了一个 ContextRefreshedEvent 的事件,这个事件是Spring内置的事件,除了该事件,Spring还内置了一些其他的事件类型,分别在以下情况下触发:
- ContextRefreshedEvent:
- 当应用程序上下文被刷新时触发。这个事件在ApplicationContext初始化或刷新时被发布,适用于执行初始化操作和启动后的后续处理。例如,初始化缓存、预加载数据等。
- ContextStartedEvent:
- 当应用程序上下文启动时触发。这个事件在调用ApplicationContext的start()方法时被发布,适用于在应用程序启动时执行特定的操作。例如,启动定时任务、启动异步消息处理等。
- ContextStoppedEvent:
- 当应用程序上下文停止时触发。这个事件在调用ApplicationContext的stop()方法时被发布,适用于在应用程序停止时执行清理操作。例如,停止定时任务、关闭数据库连接等。
- ContextClosedEvent:
- 当应用程序上下文关闭时触发。这个事件在调用ApplicationContext的close()方法时被发布,适用于在应用程序关闭前执行最后的清理工作。例如,释放资源、保存日志等。
- RequestHandledEvent:
- 在Web应用程序中,当一个HTTP请求处理完成后触发。这个事件在Spring的DispatcherServlet处理完请求后被发布,适用于记录请求日志、处理统计数据等。
- ApplicationEvent:
- 这是一个抽象的基类,可以用于定义自定义的应用程序事件。你可以创建自定义事件类,继承自ApplicationEvent,并定义适合你的应用场景的事件类型。
3. 自定义事件与监听器Demo
在学习完上面的内容后,我们现在可以手动写个Spring的事件,以及对应的监听器的demo了
3.1 构建两个自定义事件
建立继承自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; } }
3.2 构建监听
我们选用@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()); // 在这里可以执行一些其他操作 } }
3.3 发布事件
@Component public class CustomEventPublisher implements ApplicationContextAware, ApplicationListener<ContextRefreshedEvent> { 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); } }