《知识点扫盲 · 监听器 Listener》

📢 大家好,我是 【战神刘玉栋】,有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 监听器的用法,仅供参考。
监听器用的最多的场景,还是初始化监听器,程序启动之前执行一些诸如,数据加载到缓存等动作。
💗 后续企业实战或源码分析会详细展开。

  • 21
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

战神刘玉栋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值