📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗
🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数,如需交流,欢迎留言评论。👍
文章目录
写在前面的话
上几篇博文介绍了拦截器 Interceptor和过滤器 Filter,这边接着介绍一下监听器、切面等内容,先把这一系列补充完毕,之前列的类目太广,精力有限,导致很多其他系列没接着更新,被粉丝催促。还是经验不足导致,后续改善。
话分两头,本篇文章让我们进入监听器 Listener的世界。
监听器简介
监听器是一种常见的设计模式,广泛应用于各种编程场景中。不同的框架和库都有自己的监听器机制,适用于不同类型的事件处理需求(这点和前面介绍的拦截器很像)。通过监听器,开发者可以实现事件驱动的编程模型,解耦事件的产生和处理逻辑,从而提高代码的可维护性和扩展性。
简单来说, 监听器用于监听一些重要事件的发生,以便在事情发生前、发生后可以做一些必要的处理。
场景举例:项目启动时,需要做一些预处理和初始化操作,如缓存预加载等,服务关闭时,清理线程资源等;
Servlet 监听器
Java Servlet 规范中定义了几种类型的监听器,用于监听 Web 应用程序生命周期中的各种事件。
下面展开介绍常见的几种。
1、ServletContextListener 和 ServletContextAttributeListener
ServletContextListener 可以监听到 ServletContext 的创建和销毁,而 ServletContextAttributeListener 可以监听到ServletContext 中属性的新增、移除和属性值的替换。
2、HttpSessionListener 和 HttpSessionAttributeListener
HttpSessionListener 可以监听 HttpSession 的创建跟销毁,而 HttpSessionAttributeListener 则是对 HttpSession 中属性的监听,它可以监听到 HttpSession 新增属性、移除属性和属性值被替换时;
3、ServletRequestListener 和 ServletRequestAttributeListener
ServletRequestListener 可见监听 Request 的创建和销毁;而 ServletRequestAttributeListener 可以对 Request 的属性进行监听;
上面三种从命名上也很好理解,逻辑也差不多,示例代码如下:
@Slf4j
@Component
public class ServletInitListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
if (log.isInfoEnabled()) {
log.info("Servlet启动初始化监听~");
}
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
if (log.isInfoEnabled()) {
log.info("Servlet启动初始化监听~");
}
}
}
如果是SpringBoot项目,那如何让其生效?有两种方式:
1、实现监听器类后,直接加上 @Component 注解,推荐这种方式,这种方式下,你可以使用 Spring 的依赖注入功能,因为监听器类是一个 Spring Bean;
2、实现监听器类后,加上 @WebListener 注解,使用该标签时,需要在启动类上使用 @ServletComponentScan 注解,这种方式就单纯当作监听器处理,不要用依赖诸如方式操作 Bean,一定要使用就借助 SpringUtil 等方式,并且要考虑容器加载顺序问题;
总之,很简单的用法,总结就是 Servlet 监听器可以监听 ServletContext、HttpSession、ServletRequest 对象的生命周期事件以及属性改变事件。其作用是监听一些重要事件的发生,监听器对象可以在事情发生前、发生后可以做一些必要的处理。
Spring 监听器
参考:链接 链接
前面介绍完了 Servlet 监听器,比较简单,而真正在 Spring 项目的开发中,用 Spring 监听器的场景更多。
先了解一下最常见的示例:
实现 ApplicationListener 接口
下面示例监听 ContextRefreshedEvent 事件,该方法在应用程序上下文被刷新时触发,该事件是 Spring 框架内置事件,由 Spring 框架执行到某一个时刻触发,我们就负责接收通知,这是其中一种观察者方式。
@Slf4j
@Component
public class SpringInitListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (event.getSource() instanceof AnnotationConfigServletWebServerApplicationContext) {
if (log.isInfoEnabled()) {
log.info("【SpringInitListener】Spring启动初始化监听,应用程序上下文已刷新~");
}
//执行主业务逻辑
doBiz();
}
}
/**
* 执行业务逻辑
*/
private void doBiz() {
}
}
使用 @EventListener 注解
该方式不需要继承或实现任何其他类,然后在它的某个方法上加上 @EventListener 注解,效果差不多。
@Slf4j
@Component
public class MySpringListener {
@EventListener(ContextRefreshedEvent.class)
public void methodA(ContextRefreshedEvent event) {
if (log.isInfoEnabled()) {
log.info("【MySpringListener】Spring启动初始化监听,应用程序上下文已刷新~");
}
}
}
Spring 常见内置事件
上面示例中,我们使用了一个 ContextRefreshedEvent 的事件,这个事件是Spring内置的事件,除了该事件,Spring还内置了一些其他的事件类型,分别在以下情况下触发。
Tips:不是很需要掌握全部的事件,按需使用。
SpringBoot Application 共支持6
种事件监听,按顺序分别是:
- ApplicationStartingEvent:在Spring最开始启动的时候触发
- ApplicationEnvironmentPreparedEvent:在Spring已准备好上下文但是上下文尚未创建的时候触发
- ApplicationPreparedEvent:在Bean定义加载之后、刷新上下文之前触发
- ApplicationStartedEvent:在刷新上下文之后、调用application命令之前触发
- ApplicationReadyEvent:在调用applicaiton命令之后触发
- ApplicationFailedEvent:在启动Spring发生异常时触发
Spring 的5
个标准事件:
- 上下文更新事件(ContextRefreshedEvent):该事件会在ApplicationContext被初始化或者更新时发布。也可以在调用ConfigurableApplicationContext 接口中的refresh()方法时被触发。
- 上下文开始事件(ContextStartedEvent):当容器调用ConfigurableApplicationContext的Start()方法开始/重新开始容器时触发该事件。
- 上下文停止事件(ContextStoppedEvent):当容器调用ConfigurableApplicationContext的Stop()方法停止容器时触发该事件。
- 上下文关闭事件(ContextClosedEvent):当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例Bean都被销毁。
- 请求处理事件(RequestHandledEvent):在Web应用中,当一个http请求(request)结束触发该事件。
自定义事件与监听器
就下面三个步骤,可以直接看代码,前面示例翻新一下:
Step1、建立继承自ApplicationEvent的自定义事件类;
Step2、使用@EventListener注解来实现监听;
Step3、发布事件;
@Slf4j
@Component
public class MySpringListener {
/**
* 监听Spring内置事件
*/
@EventListener(ContextRefreshedEvent.class)
public void methodA(ContextRefreshedEvent event) {
if (log.isInfoEnabled()) {
log.info("【MySpringListener】Spring启动初始化监听,应用程序上下文已刷新~");
}
}
/**
* 监听自定义事件
*/
@EventListener(MyCustomEvent.class)
public void methodB(MyCustomEvent event) {
log.info("========我监听到自定义事件了:" + event.getMessage());
}
/**
* 再次监听自定义事件
*/
@EventListener(MyCustomEvent.class)
public void methodC(MyCustomEvent event) {
log.info("========我监听到自定义事件了:" + event.getMessage());
}
}
@Getter
@Setter
public class MyCustomEvent extends ApplicationEvent {
private String message;
public MyCustomEvent(Object source, String message) {
super(source);
this.message = message;
}
}
@Slf4j
@Component
public class SpringInitListener implements ApplicationContextAware, ApplicationListener<ContextRefreshedEvent> {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (event.getSource() instanceof AnnotationConfigServletWebServerApplicationContext) {
if (log.isInfoEnabled()) {
log.info("【SpringInitListener】Spring启动初始化监听,应用程序上下文已刷新~");
}
// 发布自定义事件
MyCustomEvent myCustomEvent = new MyCustomEvent(applicationContext, "这是自定义事件,我发布了");
applicationContext.publishEvent(myCustomEvent);
// 执行主业务逻辑
doBiz();
}
}
/**
* 执行业务逻辑
*/
private void doBiz() {
}
}
Spring 监听器原理分析
这里简单说一下Spring监听器的实现原理。
Spring 的监听器用到了观察者模式、工厂模式、适配器模式,以观察者模式为主,可用于松散耦合,改进代码管理和潜在的复用。
通俗来说,就是创建一个主题对象,主题对象维护一个观察者列表,当主题对象的状态发生变化时,它会遍历观察者列表,并调用每个观察者的通知方法。观察者接收到通知后,根据通知进行相应的更新操作。
举例说明,当抖音的某个海贼王博主,很多粉丝关注了它的海贼王专栏,就属于它的观察者,当博主更新了海贼王1100集漫画的讲解视频,这些粉丝在抖音在线的状态,会收到该视频的更新通知,可以快速点击进去,一睹为快。
这个模式其实很好理解,可能很多场景都遇到过它的变种。消息中心的事件驱动机制,Redis-Key的发布订阅机制,都是基于观察者模式扩展而来,运用范围相当广。
由于本篇文章是知识点扫盲系统,属于技术入门,后续企业实战或源码分析会详细展开。
实战 · 初始化监听器拓展
本来内容写到这边差不多了,但是感觉好像太少一些,实战部分的还是分享一些。
显然,这个用法很简单,关于 ContextRefreshedEvent 的初始化监听,作为具体某个模块的开发人员完全可以写一个 Spring 监听器类,完成自己想要的初始化动作,So easy~
但该用法存在一些局限性:
首先,该操作是同步的,若初始化执行的程序逻辑耗时较多,会影响整个服务的启动时长,进而引发一系列问题,例如KS8误判启动失败等;
其次,该操作中,只要业务逻辑存在未把控到位的情况,意外抛出了异常,那么将直接导致程序启动失败,这可能是违背初衷的;
总之,这两点因素带来的影响都很大,那么作为一个框架搭建人员,如何应对这些现象,如何给开发人员更灵活的编码体验,这个是需要我们思考的。
参考:《框架封装 · 自定义初始化事件》
其他监听器
除了上面介绍的 Spring 和 Servlet 之外,其他技术也有监听器,简单介绍一些,不展开。
1、JavaScript 监听器
JavaScript 广泛应用于前端开发,浏览器提供了多种事件监听机制。
关键词: addEventListener
2、Kafka 监听器
Apache Kafka 提供了事件监听机制,用于处理消息队列中的事件。
@KafkaListener(topics = "myTopic", groupId = "myGroup")
public void listen(String message) {
System.out.println("Received message: " + message);
}
3、Hibernate 监听器
Hibernate 提供了事件监听器,用于监听实体对象的生命周期事件。
//PreInsertEventListener、PostInsertEventListener:监听实体插入事件。
public class MyPreInsertEventListener implements PreInsertEventListener {
@Override
public boolean onPreInsert(PreInsertEvent event) {
return false;
}
}
总结陈词
上文介绍了监听器的用法,主要介绍了 Spring 监听器的用法,仅供参考。
监听器用的最多的场景,还是初始化监听器,程序启动之前执行一些诸如,数据加载到缓存等动作。
💗 后续企业实战或源码分析会详细展开。