事件监听(基于SpringBoot示例)

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

概述

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

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

详细(示例)说明

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

import org.springframework.context.ApplicationEvent;

/**
 * 自定义事件
 *
 * 注:继承ApplicationEvent即可。
 *
 * @author JustryDeng
 * @date 2019/11/19 6:36
 */
public class MyEvent extends ApplicationEvent {


    /**
     * 构造器
     *
     * @param source
     *            该事件的相关数据
     *
     * @date 2019/11/19 6:40
     */
    public MyEvent(Object source) {
        super(source);
    }
}

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

public class MyEvent extends ApplicationEvent {
    
    /** 事件相关的数据 */
    @Getter
    private final EventDataDTO eventDataDTO;
    
    public MyEvent(EventDataDTO source) {
        super(source);
        this.eventDataDTO = source;
    }
}

第二步自定义监听器。

  • 方式一(推荐) 通过实现ApplicationListener来自定义监听器,其中E为此监听器要监听的事件。
    /**
     * 自定义监听器
     *
     * 注:实现ApplicationListener<E extends ApplicationEvent>即可,
     *    其中E为此监听器要监听的事件。
     *
     * @author JustryDeng
     * @date 2019/11/19 6:44
     */
    public class MyListenerOne implements ApplicationListener<MyEvent> {
    
        /**
         * 编写处理事件的逻辑
         *
         * @param event
         *            当前事件对象
         */
        @Override
        public void onApplicationEvent(MyEvent event) {
            /// 当前事件对象携带的数据
            /// Object source = event.getSource();
            System.out.println(
                    "线程-【" + Thread.currentThread().getName() + "】 => "
                    + "监听器-【MyListenerOne】 => "
                    + "监听到的事件-【" + event + "】"
            );
    
        }
    }
    
    注:需要重写onApplicationEvent方法来自定义相关事件的处理逻辑。
  • 方式二 在某个方法上使用@EventListener注解即可。
    import org.springframework.context.event.EventListener;
    
    /**
     * 自定义监听器
     *
     * 注:在某个方法上使用@EventListener注解即可。
     *    追注一: 这个方法必须满足: 最多能有一个参数。
     *    追注二:    若只是监听一种事件,那么这个方法的参数类型应为该事
     *           件对象类;P.S.:该事件的子类事件,也属于该事件,也会被监听到。
     *              若要监听多种事件,那么可以通过@EventListener注解
     *           的classes属性指定多个事件,且保证这个方法无参;
     *
     *
     *
     * @author JustryDeng
     * @date 2019/11/19 6:44
     */
    public class MyListenerTwo {
    
        /**
         * 编写处理事件的逻辑
         *
         * @param event
         *            当前事件对象
         */
        @EventListener
        public void abc(MyEvent event) {
            /// 当前事件对象携带的数据
            /// Object source = event.getSource();
            System.out.println(
                    "线程-【" + Thread.currentThread().getName() + "】 => "
                            + "监听器-【MyListenerTwo】 => "
                            + "监听到的事件-【" + event + "】"
            );
        }
    
    }
    
    注:在某个方法上使用@EventListener注解即可。
    注:被@EventListener注解方法必须满足: 最多能有一个参数。
    注:若只是监听一种事件,那么这个方法的参数类型应为该事件对象类;
           P.S.:该事件的子类事件,也属于该事件,也会被监听到。
    注:若要监听多种事件,那么可以通过@EventListener注解的classes属性指定多个事件,
           且保证这个方法无参。
    提示:还可通过设置@EventListener注解的condition属性来对事件进行选择性处理(P.S.:当然用代码也能做到)。

第三步注册监听器。

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

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

  • 方式②示例
    在这里插入图片描述
    或者

    在这里插入图片描述

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

    在这里插入图片描述

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

        使用实现了ApplicationEventPublisher接口的类(常用ApplicationContext)的publishEvent(ApplicationEvent event)方法发布事件。
在这里插入图片描述
注:SpringBoot启动时,返回的ConfigurableApplicationContext是ApplicationContext的子类,所以如果想在SpringBoot启动后就立马发布事件的话,可以这样写:
在这里插入图片描述

验证测试

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

同步监听与异步监听

同步监听

        按上文中的配置,实际上默认是同步监听机制。所谓同步监听,即:业务逻辑与监听器的逻辑在同一个线程上、按顺序执行

  • 举例说明一:
            假设某线程α,线程β都有发布各自的事件,那么α线程发布的事件会被α线程进行监听器逻辑处理,β线程发布的事件会被β线程进行监听器逻辑处理。

  • 举例说明二
            假设某线程β要做的总体逻辑流程是,做A => 发事件x => 做B => 发事件y => 发事件z => 返回响应,那么同步监听下是这样的: 线程β先做A,(A做完后)接着做事件x对应的监听器逻辑,(x的监听器逻辑做完后,线程β才能)接着做B,(B做完后)接着做事件y对应的监听器逻辑,(y的监听器逻辑做完后,线程β才能)接着做事件z对应的监听器逻辑,最后才能返回响应。

异步监听

        如果需要异步监听,那么需要开启异步功能(见下文示例),所谓异步监听即:业务逻辑与监听器的逻辑不在同一个线程上,处理监听器逻辑的事会被线程池中的某些线程异步并发着做

  • 举例说明
            假设某线程β要做的总体逻辑流程是,做A => 发事件x => 做B => 发事件y => 发事件z => 返回响应,那么异步监听下是这样的:线程β先做A,(A做完后)发布事件x,(线程β不管x的监听器逻辑)紧接着做B,(B做完后,线程β)接着发布事件y,(线程β不管y的监听器逻辑)紧接着发布事件z,(线程β不管z的监听器逻辑)紧接着直接返回响应。而线程β发布的事件对应的各个监听器逻辑,会由线程池中的某些线程异步并发着做

开启异步功能

  1. 第一步:@EnableAsync启用异步功能。
    在这里插入图片描述
  2. 第二步:@Async指定异步方法。
    在这里插入图片描述
  3. 第三步(可选): 自定义配置线程池执行器Executor(提示:配置Executor后,在使用@Async注解时,可以通过设置其属性来指定使用哪一个Executor)。
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
    
    import java.util.concurrent.Executor;
    import java.util.concurrent.ThreadPoolExecutor;
    
    /**
     * 自定义线程池Executor。
     *
     * 注: 我们常用的ExecutorService就继承自Executor。
     *
     * 注:关于线程池的各个参数的介绍、各个参数的关系,可详见<linked>https://blog.csdn.net/justry_deng/article/details/89331199</linked>
     *
     * @author JustryDeng
     * @date 2019/11/25 11:05
     */
    @Configuration
    public class SyncExecutor {
    
        /** 核心线程数 */
        private static final int CORE_POOL_SIZE = 5;
    
        /** 最大线程数 */
        private static final int MAX_POOL_SIZE = 100;
    
        /** 阻塞队列容量 */
        private static final int QUEUE_CAPACITY = 20;
    
        @Bean
        public Executor myAsyncExecutor() {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            executor.setCorePoolSize(CORE_POOL_SIZE);
            executor.setMaxPoolSize(MAX_POOL_SIZE);
            executor.setQueueCapacity(QUEUE_CAPACITY);
            executor.setThreadNamePrefix("JustryDeng-Executor-");
            // 设置,当任务满额时将新任务(如果有的话),打回到原线程去执行。
            executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
            executor.initialize();
            return executor;
        }
    
    }
    

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

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

在这里插入图片描述


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


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

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

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

  • 10
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Spring Boot 提供了一种简单且方便的方式来处理应用程序中的事件。它基于观察者模式和发布-订阅机制实现了事件驱动的编程模型。以下是 Spring Boot 中处理事件的常见方式: 1. 定义事件类:首先,您需要定义一个事件类,它包含了您希望在应用程序中触发的事件的相关信息。 ```java public class CustomEvent { private String message; // getters and setters } ``` 2. 创建事件发布者:接下来,您需要创建一个事件发布者,用于触发和发布事件。 ```java @Component public class CustomEventPublisher { private final ApplicationEventPublisher applicationEventPublisher; public CustomEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; } public void publishEvent(String message) { CustomEvent event = new CustomEvent(message); applicationEventPublisher.publishEvent(event); } } ``` 3. 创建事件监听器:然后,您可以创建一个或多个事件监听器来处理发布事件。这些监听器需要实现 `ApplicationListener` 接口。 ```java @Component public class CustomEventListener implements ApplicationListener<CustomEvent> { @Override public void onApplicationEvent(CustomEvent event) { // 处理事件逻辑 String message = event.getMessage(); System.out.println("Received custom event with message: " + message); } } ``` 4. 发布事件:最后,在适当的地方,您可以通过调用发布者的方法来触发事件。 ```java @RestController public class MyController { private final CustomEventPublisher eventPublisher; public MyController(CustomEventPublisher eventPublisher) { this.eventPublisher = eventPublisher; } @GetMapping("/publish") public String publishEvent() { eventPublisher.publishEvent("Hello, custom event!"); return "Event published"; } } ``` 这样,当访问 `/publish` 路径时,将触发事件,并且事件监听器将接收到该事件并执行相应的逻辑。 这是 Spring Boot 中处理事件的基本示例,您可以根据实际需求进行扩展和定制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值