事件监听(基于SpringBoot示例)

小灰灰 2023-06-16 07:57 150阅读 0赞

在实际开发中,业务代码与辅助代码的解耦是一个热点话题,如:通过AOP记录入参出参、使用事件监听记录错误信息等是一个不错的选择。

概述:

  1. 事件的发布与监听从属于观察者模式;和MQ相比,事件的发布与监听偏向于处理“体系内”的某些逻辑。**事件的发布与监听总体分为以下几个步骤:**

























步骤 相关事宜
1 定义事件
2 定义(用于处理某种事件的)监听器
3 注册监听器
4 发布事件(,监听到了该事件的监听器自动进行相关逻辑处理)

详细(示例)说明:

第一步:通过继承ApplicationEvent来自定义事件。

  1. import org.springframework.context.ApplicationEvent;
  2. /** * 自定义事件 * * 注:继承ApplicationEvent即可。 * * @author JustryDeng * @date 2019/11/19 6:36 */
  3. public class MyEvent extends ApplicationEvent {
  4. /** * 构造器 * * @param source * 该事件的相关数据 * * @date 2019/11/19 6:40 */
  5. public MyEvent(Object source) {
  6. super(source);
  7. }
  8. }

注:构造器的参数为该事件的相关数据对象,监听器可以获取到该数据对象,进而进行相关逻辑处理。

第二步:自定义监听器。

  • 方式一(推荐): 通过实现ApplicationListener来自定义监听器,其中E为此监听器要监听的事件。

    1. /** * 自定义监听器 * * 注:实现ApplicationListener<E extends ApplicationEvent>即可, * 其中E为此监听器要监听的事件。 * * @author JustryDeng * @date 2019/11/19 6:44 */
    2. public class MyListenerOne implements ApplicationListener<MyEvent> {
    3. /** * 编写处理事件的逻辑 * * @param event * 当前事件对象 */
    4. @Override
    5. public void onApplicationEvent(MyEvent event) {
    6. /// 当前事件对象携带的数据
    7. /// Object source = event.getSource();
    8. System.out.println(
    9. "线程-【" + Thread.currentThread().getName() + "】 => "
    10. + "监听器-【MyListenerOne】 => "
    11. + "监听到的事件-【" + event + "】"
    12. );
    13. }
    14. }

    注:需要重写onApplicationEvent方法来自定义相关事件的处理逻辑。

  • 方式二: 在某个方法上使用@EventListener注解即可。

    1. import org.springframework.context.event.EventListener;
    2. /** * 自定义监听器 * * 注:在某个方法上使用@EventListener注解即可。 * 追注一: 这个方法必须满足: 最多能有一个参数。 * 追注二: 若只是监听一种事件,那么这个方法的参数类型应为该事 * 件对象类;P.S.:该事件的子类事件,也属于该事件,也会被监听到。 * 若要监听多种事件,那么可以通过@EventListener注解 * 的classes属性指定多个事件,且保证这个方法无参; * * * * @author JustryDeng * @date 2019/11/19 6:44 */
    3. public class MyListenerTwo {
    4. /** * 编写处理事件的逻辑 * * @param event * 当前事件对象 */
    5. @EventListener
    6. public void abc(MyEvent event) {
    7. /// 当前事件对象携带的数据
    8. /// Object source = event.getSource();
    9. System.out.println(
    10. "线程-【" + Thread.currentThread().getName() + "】 => "
    11. + "监听器-【MyListenerTwo】 => "
    12. + "监听到的事件-【" + event + "】"
    13. );
    14. }
    15. }

    注:在某个方法上使用@EventListener注解即可。
    注:被@EventListener注解方法必须满足: 最多能有一个参数。
    注:若只是监听一种事件,那么这个方法的参数类型应为该事件对象类;

    1. P.S.:该事件的子类事件,也属于该事件,也会被监听到。

    注:若要监听多种事件,那么可以通过@EventListener注解的classes属性指定多个事件,

    1. 且保证这个方法无参。

    提示:还可通过设置@EventListener注解的condition属性来对事件进行选择性处理(P.S.:当然用代码也能做到)。

第三步:注册监听器。

  1. *所谓注册监听器,其实质就是将监听器进行IOC处理,让容器管理监听器的生命周期。* SpringBoot中,有以下方式可以达到这些效果:





























方式 具体操作 适用范围 能否搭配@Async注解,进行异步监听 优先级(即:当这三种方式注册的(监听同一类型事件的)监听器都同时存在时,那么一个事件发布后,哪一种方式先监听到)
在SpringBoot启动类的main方法中,使用SpringApplication的addListeners方法进行注册
提示:当在进行单元测试时,(由于不会走SpringBoot启动的类main方法,所以)此方式不生效。
实现了ApplicationListener的监听器 不能 取决于Bean被Ioc的先后顺序。可通过设置@Order优先级的方式,来达到调整监听器优先级的目的。
提示:实际开发中,考虑优先级的意义不大。
(推荐) 通过@Component或类似注解,将监听器(或监听器方法所在的)类IOC 实现了ApplicationListener的监听器以及通过@EventListener注解指定的监听器
在SpringBoot配置文件(.properties文件或.yml文件)中指定监听器(或监听器方法所在的)类
注:多个监听器,使用逗号分割即可。
实现了ApplicationListener的监听器 不能
  • 方式①示例:
    在这里插入图片描述
  • 方式②示例:
    在这里插入图片描述
    或者

    在这里插入图片描述

  • 方式③示例:
    在这里插入图片描述
    注:为了美观,建议换行(在properties文件中使用\换行):

    在这里插入图片描述

第四步:发布事件,触发监听。

  1. 使用实现了ApplicationEventPublisher接口的类(常用ApplicationContext)的publishEvent(ApplicationEvent event)方法发布事件。

在这里插入图片描述
注:SpringBoot启动时,返回的ConfigurableApplicationContext是ApplicationContext的子类,所以如果想在SpringBoot启动后就立马发布事件的话,可以这样写:
在这里插入图片描述

验证测试:

  • 按上述示例监听逻辑,编写示例:
    在这里插入图片描述
  • 运行main方法,启动SpringBoot:
    在这里插入图片描述
  • 控制台输出:
    在这里插入图片描述
    SpringBoot中事件的发布与监听初步学习完毕!

同步监听与异步监听:

同步监听:

  1. 按上文中的配置,实际上默认是同步监听机制。所谓同步监听,即:业务逻辑与监听器的逻辑在同一个线程上、按顺序执行。
  • 举例说明一:
    1. 假设某线程α,线程β都有发布各自的事件,那么α线程发布的事件会被α线程进行监听器逻辑处理,β线程发布的事件会被β线程进行监听器逻辑处理。
  • 举例说明二:
    1. 假设某线程β要做的总体逻辑流程是,**做A => 发事件x => B => 发事件y => 发事件z => 返回响应**,那么同步监听下是这样的: **线程β先做A,(A做完后)接着做事件x对应的监听器逻辑,(x的监听器逻辑做完后,线程β才能)接着做B,(B做完后)接着做事件y对应的监听器逻辑,(y的监听器逻辑做完后,线程β才能)接着做事件z对应的监听器逻辑,最后才能返回响应。**

异步监听:

  1. 如果需要异步监听,那么需要开启异步功能(见下文示例),所谓异步监听即:业务逻辑与监听器的逻辑不在同一个线程上,处理监听器逻辑的事会被线程池中的某些线程异步并发着做。
  • 举例说明:
    1. 假设某线程β要做的总体逻辑流程是,**做A => 发事件x => B => 发事件y => 发事件z => 返回响应**,那么异步监听下是这样的:**线程β先做A,(A做完后)发布事件x,(线程β不管x的监听器逻辑)紧接着做B,(B做完后,线程β)接着发布事件y,(线程β不管y的监听器逻辑)紧接着发布事件z,(线程β不管z的监听器逻辑)紧接着直接返回响应**。而**线程β发布的事件对应的各个监听器逻辑,会由线程池中的某些线程异步并发着做**。

开启异步功能:

  1. 第一步:@EnableAsync启用异步功能。
    在这里插入图片描述
  2. 第二步:@Async指定异步方法。
    在这里插入图片描述
  3. 第三步(可选): 自定义配置线程池执行器Executor(提示:配置Executor后,在使用@Async注解时,可以通过设置其属性来指定使用哪一个Executor)。

    1. import org.springframework.context.annotation.Bean;
    2. import org.springframework.context.annotation.Configuration;
    3. import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
    4. import java.util.concurrent.Executor;
    5. import java.util.concurrent.ThreadPoolExecutor;
    6. /** * 自定义线程池Executor。 * * 注: 我们常用的ExecutorService就继承自Executor。 * * 注:关于线程池的各个参数的介绍、各个参数的关系,可详见<linked>https://blog.csdn.net/justry_deng/article/details/89331199</linked> * * @author JustryDeng * @date 2019/11/25 11:05 */
    7. @Configuration
    8. public class SyncExecutor {
    9. /** 核心线程数 */
    10. private static final int CORE_POOL_SIZE = 5;
    11. /** 最大线程数 */
    12. private static final int MAX_POOL_SIZE = 100;
    13. /** 阻塞队列容量 */
    14. private static final int QUEUE_CAPACITY = 20;
    15. @Bean
    16. public Executor myAsyncExecutor() {
    17. ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    18. executor.setCorePoolSize(CORE_POOL_SIZE);
    19. executor.setMaxPoolSize(MAX_POOL_SIZE);
    20. executor.setQueueCapacity(QUEUE_CAPACITY);
    21. executor.setThreadNamePrefix("JustryDeng-Executor-");
    22. // 设置,当任务满额时将新任务(如果有的话),打回到原线程去执行。
    23. executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    24. executor.initialize();
    25. return executor;
    26. }
    27. }

注:@Async指定异步方法时,就可以选择使用哪一个线程池Executor了,如:
在这里插入图片描述

同步监听与异步监听的比较:

在这里插入图片描述


SpringBoot中事件的发布与监听学习完毕!

^_^ 如有不当之处,欢迎指正

^_^ 测试代码托管链接
https://github.com/JustryDeng…Abc_EventListener_Demo

^_^ 本文已经被收录进《程序员成长笔记》 ,笔者JustryDeng

发表评论

表情:
评论列表 (有 0 条评论,150人围观)

还没有评论,来说两句吧...

相关阅读

    相关 事件监听(基于SpringBoot示例)

    > 在实际开发中,业务代码与辅助代码的解耦是一个热点话题,如:通过AOP记录入参出参、使用事件监听记录错误信息等是一个不错的选择。 概述:         事件的发布与

    相关 springboot -03 事件监听

      在实际的开发中,常常遇到这种场景: 当某件事情完成后,需要通知其他的模块进行相应的处理。我们可以一个一个的发送请求去通知,但是更好的处理方式是通过事件监听来完成。事件监听