springboot监听器的使用(ApplicationListener、SmartApplicationListener、@EventListener)

news2024/11/30 12:28:22

目录

  • 前言
  • 1. ApplicationListener
    • 1. 简单的全局监听
    • 2. 定时任务
    • 3. 监听自定义事件
  • 2. SmartApplicationListener
    • 1. 简单使用
    • 2. 方法介绍
  • 3. @EventListener

前言

监听器: 当某个事件触发的时候,就会执行的方法块。

springboot提供了两个接口来实现监听:ApplicationListenerSmartApplicationListener,如下图。显而易见,SmartApplicationListenerApplicationListener 的子类,故而其功能要强于 ApplicationListener

当然,springboot很贴心地提供了一个 @EventListener 注解来实现监听。

1. ApplicationListener

1. 简单的全局监听

首先,先来简单体验一下监听器的功能。

需求: 在spring容器初始化完成之后就开始监听,并打印日志。

实现:

  • 准备springboot工程(依赖:springboot、lombok)

  • 写一个监听器

    @Slf4j
    @Component
    public class MyTask implements ApplicationListener<ContextRefreshedEvent> {
    
        private static boolean aFlag = false;
    
        @Override
        public void onApplicationEvent(ContextRefreshedEvent event) {
            if (!aFlag) {
                aFlag = true;
                log.info("我已经监听到了");
            }
        }
    }
    
  • 启动项目,控制台输出如下:

现在来说一下为什么要这么写监听器:

  • 实现接口

    implements ApplicationListener<ContextRefreshedEvent>
    

    自定义监听器需要实现 ApplicationListener<E extends ApplicationEvent> 接口

    ContextRefreshedEvent 是一个事件,它会在 spring容器初始化完成 之后被触发,所以监听器就会在 spring容器初始化完成之后开始监听,所以这就是所谓的全局监听

  • 标志位 aFlag

    private static boolean aFlag = false;
    

    aFlag 是一个启动标志
    因为web应用会出现父子容器,这样就会触发两次监听任务,所以需要一个标志位,保证监听任务(log.info(“我已经监听到了”))只会触发一次

2. 定时任务

需求: 实现一个定时任务,每间隔5秒、10秒、15秒、20秒、25秒、30秒、40秒、50秒、60秒,在控制台循环打印一次日志。

实现: 可通过多线程的方式实现

  • 新建一个类 TimerRunner 继承 Runnable

  • 5秒到60秒的间隔可以通过 枚举类实现

    private enum TimerEnum {
        // 第5秒打印
        FIRST(1, 5 ),
        // 第10秒打印
        SECOND(2, 10),
        // 第15秒打印
        THIRD(3, 15),
        // 第20秒打印
        FOURTH(4, 20),
        // 第25秒打印
        FIFTH(5, 25),
        // 第30秒打印
        SIXTH(6, 30),
        // 第40秒打印
        SEVENTH(7, 40),
        // 第50秒打印
        EIGHTH(8, 50),
        // 第60秒打印
        NINTH(9, 60);
    
        private Integer count;
    
        private Integer time;
    
        TimerEnum(Integer count, Integer time) {
            this.count = count;
            this.time = time;
        }
    
        public Integer getCount() {
            return count;
        }
    
        public Integer getTime() {
            return time;
        }
    }
    
  • TimeRunner 完整代码

    @Slf4j
    public class TimerRunner implements Runnable{
        @Override
        public void run() {
            // 打印次数
            int count = 1;
            SimpleDateFormat dateFormat= new SimpleDateFormat("hh:mm:ss");
            for (TimerEnum item: TimerEnum.values()) {
                if (count == item.getCount()) {
                    if (count != 9) {
                        log.info("时间: " + dateFormat.format(new Date()) + "第 " + count + " 次打印,还剩余 " + (9 - count) + " 次完成打印");
                        count++;
                    } else {
                        log.info("最后一次打印");
                        log.info("已完成所有打印任务!");
                    }
                }
                try {
                	// TimeUnit来sleep,可读性更好
                    TimeUnit.SECONDS.sleep(item.getTime());
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    
        private enum TimerEnum {
            // 第5秒打印
            FIRST(1, 5 ),
            // 第10秒打印
            SECOND(2, 10),
            // 第15秒打印
            THIRD(3, 15),
            // 第20秒打印
            FOURTH(4, 20),
            // 第25秒打印
            FIFTH(5, 25),
            // 第30秒打印
            SIXTH(6, 30),
            // 第40秒打印
            SEVENTH(7, 40),
            // 第50秒打印
            EIGHTH(8, 50),
            // 第60秒打印
            NINTH(9, 60);
    
            private Integer count;
    
            private Integer time;
    
            TimerEnum(Integer count, Integer time) {
                this.count = count;
                this.time = time;
            }
    
            public Integer getCount() {
                return count;
            }
    
            public Integer getTime() {
                return time;
            }
        }
    }
    
  • MyTask 代码

    @Slf4j
    @Component
    public class MyTask implements ApplicationListener<ContextRefreshedEvent> {
    
        private static boolean aFlag = false;
    
        @Override
        public void onApplicationEvent(ContextRefreshedEvent event) {
            if (!aFlag) {
                aFlag = true;
                new Thread(new TimerRunner()).start();
            }
        }
    }
    
  • 控制台输出:

3. 监听自定义事件

Spring的 ApplicationContext 提供了支持事件和代码中监听器的功能。
我们可以创建bean用来监听在 ApplicationContext 中发布的事件。ApplicationEvent 类在 ApplicationContext 接口中处理的事件,如果一个bean实现了 ApplicationListener 接口,当一个 ApplicationEvent 被发布以后,bean会自动被通知。


参考链接:https://cloud.tencent.com/developer/article/1532994

先来看一下 spring的内置事件

内置事件: 参考链接: https://blog.csdn.net/liyantianmin/article/details/81017960

事件说明
ContextRefreshedEventApplicationContext 被初始化或刷新时,该事件被发布。这也可以在 ConfigurableApplicationContext接口中使用 refresh() 方法来发生。此处的初始化是指:所有的Bean被成功装载,后处理Bean被检测并激活,所有Singleton Bean 被预实例化,ApplicationContext容器已就绪可用。
ContextStartedEvent当使用 ConfigurableApplicationContext (ApplicationContext子接口)接口中的 start() 方法启动 ApplicationContext 时,该事件被发布。你可以调查你的数据库,或者你可以在接受到这个事件后重启任何停止的应用程序。
ContextStoppedEvent当使用 ConfigurableApplicationContext 接口中的 stop() 停止 ApplicationContext 时,发布这个事件。你可以在接受到这个事件后做必要的清理的工作。
ContextClosedEvent当使用 ConfigurableApplicationContext 接口中的 close() 方法关闭 ApplicationContext 时,该事件被发布。一个已关闭的上下文到达生命周期末端;它不能被刷新或重启。
RequestHandledEvent这是一个 web-specific 事件,告诉所有 bean HTTP 请求已经被服务。只能应用于使用DispatcherServlet的Web应用。在使用Spring作为前端的MVC控制器时,当Spring处理用户请求结束后,系统会自动触发该事件。

自定义监听事件:

  • extends ApplicationEvent 自定义事件

    public class MyEvent extends ApplicationEvent {
    
        private String time = new SimpleDateFormat("hh:mm:ss").format(new Date());
        private String msg;
    
        public MyEvent(Object source, String msg) {
            super(source);
            this.msg = msg;
        }
    
        public MyEvent(Object source) {
            super(source);
        }
    
        public String getTime() {
            return time;
        }
    
        public void setTime(String time) {
            this.time = time;
        }
    
        public String getMsg() {
            return msg;
        }
    
        public void setMsg(String msg) {
            this.msg = msg;
        }
    }
    
  • 监听器

    @Slf4j
    @Component
    public class MyTask implements ApplicationListener {
    
        private static boolean aFlag = false;
    
    
        @Override
        public void onApplicationEvent(ApplicationEvent event) {
            if (event instanceof ContextRefreshedEvent) {
                log.info("监听到 ContextRefreshedEvent...");
            }
            if (event instanceof MyEvent) {
                log.info("监听到 MyEvent...");
                MyEvent myEvent = (MyEvent) event;
                System.out.println("时间:" + myEvent.getTime() + " 信息:" + myEvent.getMsg());
            }
        }
    }
    
  • 触发事件
    自定义监听事件需要主动触发

    @SpringBootApplication
    public class TaskApplication {
        public static void main(String[] args) {
            ConfigurableApplicationContext run = SpringApplication.run(TaskApplication.class, args);
            MyEvent event = new MyEvent("event", "忙中岁月忙中遣,我本愚来性不移");
            // 发布事件
            run.publishEvent(event);
        }
    }
    

    也可以这样触发,美观一点

    @SpringBootApplication
    public class TaskApplication implements CommandLineRunner {
        public static void main(String[] args) {
            SpringApplication.run(TaskApplication.class, args);
        }
    
        @Resource
        private ApplicationContext applicationContext;
    
        @Override
        public void run(String... args) throws Exception {
            MyEvent event = new MyEvent("event", "忙中岁月忙中遣,我本愚来性不移");
            // 发布事件
            applicationContext.publishEvent(event);
    
        }
    }
    
  • 控制台输出

2. SmartApplicationListener

1. 简单使用

@Slf4j
@Component
public class MyTask implements SmartApplicationListener {
    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
        return eventType == MyEvent.class || eventType == ContextRefreshedEvent.class;
    }

    @Override
    public int getOrder() {
        return 0;
    }

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ContextRefreshedEvent) {
            log.info("监听到 ContextRefreshedEvent...");
        }
        if (event instanceof MyEvent) {
            log.info("监听到 MyEvent...");
            MyEvent myEvent = (MyEvent) event;
            System.out.println("时间:" + myEvent.getTime() + " 信息:" + myEvent.getMsg());
        }
    }
}

2. 方法介绍

public interface SmartApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {

	boolean supportsEventType(Class<? extends ApplicationEvent> eventType);

	default boolean supportsSourceType(@Nullable Class<?> sourceType) {
		return true;
	}

	@Override
	default int getOrder() {
		return LOWEST_PRECEDENCE;
	}

	default String getListenerId() {
		return "";
	}

}
方法说明
supportsEventType确认当前监听器是否支持当前事件类型。
supportsSourceType确定此侦在这里插入代码片听器是否实际支持给定的源类型。
getOrder确定此侦听器在同一事件的一组侦听器中的顺序。数值越小,优先级越高。
getListenerId返回侦听器的可选标识符。

3. @EventListener

使用:

@Slf4j
@Component
public class MyTask {
    @EventListener
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ContextRefreshedEvent) {
            log.info("监听到 ContextRefreshedEvent...");
        }
        if (event instanceof MyEvent) {
            log.info("监听到 MyEvent...");
            MyEvent myEvent = (MyEvent) event;
            System.out.println("时间:" + myEvent.getTime() + " 信息:" + myEvent.getMsg());
        }
    }
}
@Slf4j
@Component
public class MyTask {
    @EventListener
    public void MyEventListener(MyEvent event) {
        log.info("监听到 MyEvent...");
        MyEvent myEvent = (MyEvent) event;
        System.out.println("时间:" + myEvent.getTime() + " 信息:" + myEvent.getMsg());
    }

    @EventListener
    public void ContextRefreshedEventListener(MyEvent event) {
        log.info("监听到 ContextRefreshedEvent...");
    }
}

指定监听事件的类型:

@EventListener(MyEvent.class)

@EventListener({MyEvent.class, ContextRefreshedEvent.class})

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/152311.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Paddle进阶实战系列(一):保险文本视觉认知问答

保险文本视觉认知问答 1.项目介绍 1.1背景 随着人工智能技术的逐渐成熟&#xff0c;计算机视觉、语音、自然语言处理等技术在金融行业的应用从广度和深度上都在加速&#xff0c;这不仅降低了金融机构的运营和风险成本&#xff0c;而且有助于提升客户的满意度&#xff0c;比如…

Rhec第二次作业

两台机器&#xff1a;第一台机器作为客户端&#xff0c;第二台机器作为服务器&#xff0c;在第一台使用rhce用户免密登录第二台机器准备两台虚拟机并保证可以ping通网络&#xff0c;ip地址不一样第一台机器配置创建rhce用户在rhce用户上&#xff0c;创建密钥对并将公钥发送给第…

JDK源码(二)ConcurrentHashMap-JDK1.7

1.背景 并发编程中&#xff0c;ConcurrentHashMap是一个使用度非常高的数据结构。 优点: 线程安全相比于HashTable和Collections.synchronizedMap()效率高&#xff0c;使用了分段锁技术。 2.ConcurrentHashMap数据结构 Segment Segment继承了ReentrantLock&#xff0c;所以它…

Java设计模式-桥接模式Bridge

传统模式 案例 要求对不同手机类型的不同品牌实现操作编程(比如:开机、关机、上网&#xff0c;打电话等)&#xff0c;如图: 类图 问题 扩展性问题(类爆炸)&#xff0c;如果我们再增加手机的样式(旋转式)&#xff0c;就需要增加各个品牌手机的类&#xff0c;同样如果我们…

【Kotlin】标准库函数 ① ( apply 标准库函数 | let 标准库函数 )

文章目录一、apply 标准库函数二、let 标准库函数Kotlin 语言中 , 在 Standard.kt 源码中 , 为所有类型定义了一批标准库函数 , 所有的 Kotlin 类型都可以调用这些函数 ; 一、apply 标准库函数 Kotlin 标准库函数 中的 apply 函数 , 该函数可以看作 实例对象 的 配置函数 , 传…

现货黄金术语汇总

有的投资者可能并不是新手&#xff0c;可能之前对股票投资的一些术语有一定的了解甚至说是经验。但是转到现货黄金市场的时候&#xff0c;还是对很多术语感到很陌生&#xff0c;下面小编针对一些与股票不一样的现货黄金术语进行介绍&#xff0c;希望可以帮助投资者们尽快"…

前言技术之swagger

一.前后端分离的特点前后端分离是的前端与后端之间的职责更加明确 后台&#xff1a; 负责业务处理 前端&#xff1a; 负责显示逻辑 在这种情况下&#xff0c;前端和后端可以分别交付给专业的开发人员去做&#xff0c;所以是必须要定义前后端直接的对接 接口&#xff0c;否则各自…

liblas读取点云,设置半透明

一&#xff0c;用Liblas读取点云数据&#xff0c;获取点云位置和颜色 二&#xff0c;将点云位置和颜色分别代入geode的位置数组和颜色数组 三&#xff0c;用glsl设置半透明。需要注意的是 1&#xff0c;颜色数组是用attribute,所以要用 geom->setVertexAttribArray(10, colo…

Wireshark抓包分析DHCP

1、DHCP简介动态主机设置协议&#xff08;英语&#xff1a;Dynamic Host Configuration Protocol&#xff0c;DHCP&#xff09;是一个局域网的网络协议&#xff0c;使用UDP协议工作&#xff0c;主要有两个用途&#xff1a;用于内部网或网络服务供应商自动分配IP地址&#xff1b…

从0到1完成一个Vue后台管理项目(十五、作业列表、表格数据方法的封装)

往期 从0到1完成一个Vue后台管理项目&#xff08;一、创建项目&#xff09; 从0到1完成一个Vue后台管理项目&#xff08;二、使用element-ui&#xff09; 从0到1完成一个Vue后台管理项目&#xff08;三、使用SCSS/LESS&#xff0c;安装图标库&#xff09; 从0到1完成一个Vu…

解决虚拟机安装 VMware Tools 灰色无法点击问题

环境&#xff1a; 主机 OS: Windows 11 虚拟机平台: VMware Workstation 17 Pro 虚拟系统: Ubuntu 22.04 1. 问题 安装好 Linux 系统后&#xff0c;想要适配桌面大小等其它功能就需要安装 VMware Tools 这个工具&#xff0c;最简单的办法就是通过虚拟机平台的 “一键安装”&a…

Java之ATM系统

目录项目介绍系统准备&#xff0c;首页设计总结开户功能总结用户登录总结用户操作页设计、查询账户、退出账户功能用户存钱取款功能转账功能密码修改、销户源代码项目介绍 系统准备&#xff0c;首页设计 总结 1、用户的账户信息&#xff0c;系统如何表示的? 定义账户类Accoun…

【CVHub】现代目标检测故事 | 40+目标检测网络架构大盘点!从基础架构ResNet到最强检测器Yolov7再到最新部署神器GhostNetV2

本文来源“CVHub”公众号&#xff0c;侵权删&#xff0c;干货满满。 作者丨派派星 来源丨CVHub 原文链接&#xff1a;现代目标检测故事 | 40种网络架构大盘点&#xff01;从基础架构ResNet到最强检测器Yolov7再到最新部署神器GhostNetV2 导读 目标检测是指在图像或视频中分…

PHY6230 高性价比低功耗高性能 集成32-bit MCU BLE5.2+2.4G芯片

PHY6230 是一款高性价比低功耗高性能Bluetooth LE 5.2系统级芯片&#xff0c;集成32-bit高性能低功耗MCU&#xff0c;16KB OTP&#xff0c;8KB Retention SRAM和64KB ROM&#xff0c;可选EEPROM。内置高性能多模射频收发机最大发射功率10dBm&#xff0c;BLE 1Mbps速率下接收灵敏…

快手发布2022直播生态报告,运营人速览

1、快手电商推出2023年直播间联合补贴活动1月5日&#xff0c;快手电商推出2023年直播间联合补贴活动。该活动主要目的是助力主播完成更高销售额&#xff0c;报名成功后&#xff0c;平台将对直播间内的一部分活跃用户发放10%-16%折扣率的满减优惠券&#xff0c;成本由平台和主播…

【BUG解决方案】jQuery数组中包含数据,但通过 .length 获得的数组长度始终为0

0. BUG展示 var lels []; for (var i 0; i < maxDevNums 1; i) {lels.push([]); } $.ajax({type : "post",async : true,url : "/sc/comb/history/data",data : {},dataType : "json",success : function (result) {if (result) {for (le…

FFmpeg 集成 x265 编译及解码

x265 是一个免费的软件库和应用程序&#xff0c;用于将视频流编码为 H.265/MPEG-H HEVC 压缩格式&#xff0c;并在 GNU GPL 条款下发布。 FFmpeg 为了支持 H.265 编、解码可以集成 x265 编译&#xff0c;在编译 FFmpeg 之前需要先编译 x265&#xff0c;但并不是所有的版本都能…

Python一轮知识拾遗

目录 字符串格式化 %格式符 format字符串格式化 三元条件运算符 可迭代对象 break和continue语句 enumerate函数 序列封包 序列解包 部分序列解包 append.列表和extend.列表的区别 字符串格式化 通过字符串的格式化&#xff0c;可以输出特定格式的字符串。 (1) 格式化…

为什么要申报绿色工厂?

一、什么是绿色工厂&#xff1f; 绿色工厂是指实现了用地集约化、生产洁净化、废物资源化、能源低碳化的工厂。 二、为什么要申报绿色工厂&#xff1f; 1、政策导向&#xff0c;发展趋势 发展绿色工厂是顺应全球绿色发展的大趋势&#xff0c;符合国家政策导向。 2、荣誉称号…

[笔记]Windows Cyswin ssh配置及远程控制

文章目录前言一、配置1.1 安装 Cygwin1.2 Cygwin安装时搜索安装ssh1.3 添加cygwin安装目录至Path环境变量1.4 配置 SSHD 服务1.5 添加 sshd连接账号二、使用2.1 使用配置的连接账号进行登录2.2 连接远程主机三、常见问题3.1 ssh on cygwin和openssh 冲突 提示 Host key verific…