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

news/2025/3/19 12:08:39/文章来源:https://www.cnblogs.com/kelelipeng/p/18503008

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

https://blog.csdn.net/m0_54355172/article/details/128592476   

 

 

目录

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

 

前言

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

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

 

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

1. ApplicationListener

1. 简单的全局监听

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

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

实现:

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

  • 写一个监听器

@Slf4j
@Component
public class MyTask implements ApplicationListener<ContextRefreshedEvent> {private static boolean aFlag = false;@Overridepublic 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{@Overridepublic 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;@Overridepublic 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

事件 说明
ContextRefreshedEvent ApplicationContext 被初始化或刷新时,该事件被发布。这也可以在 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;@Overridepublic 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);}@Resourceprivate ApplicationContext applicationContext;@Overridepublic void run(String... args) throws Exception {MyEvent event = new MyEvent("event", "忙中岁月忙中遣,我本愚来性不移");// 发布事件applicationContext.publishEvent(event);}
}

  

控制台输出

 

2. SmartApplicationListener

1. 简单使用

@Slf4j
@Component
public class MyTask implements SmartApplicationListener {@Overridepublic boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {return eventType == MyEvent.class || eventType == ContextRefreshedEvent.class;}@Overridepublic int getOrder() {return 0;}@Overridepublic 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;}@Overridedefault int getOrder() {return LOWEST_PRECEDENCE;}default String getListenerId() {return "";}}

  

方法 说明

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


3. @EventListener
使用:

@Slf4j
@Component
public class MyTask {@EventListenerpublic 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 {@EventListenerpublic void MyEventListener(MyEvent event) {log.info("监听到 MyEvent...");MyEvent myEvent = (MyEvent) event;System.out.println("时间:" + myEvent.getTime() + " 信息:" + myEvent.getMsg());}@EventListenerpublic void ContextRefreshedEventListener(MyEvent event) {log.info("监听到 ContextRefreshedEvent...");}
}

  

指定监听事件的类型:

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

  

原文链接:https://blog.csdn.net/m0_54355172/article/details/128592476

 

文章知识点与官方知识档案匹配,可进一步学习相关知识
云原生入门技能树首页概览20512 人正在系统学习中

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

wordpress接入腾讯云COS,50G月免费流量

对象存储COS是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景,适用于网站需要实时访…

千兆网卡与万兆网卡区别是什么

千兆网卡与万兆网卡区别:一、传输速度区别;二、物理接口区别;三、传输距离区别;四、价格区别;五、功耗区别;六、适用场景区别。传输速度区别在于,千兆网卡的传输速度为1 Gbps,而万兆网卡的传输速度为10 Gbps。一、传输速度区别 千兆网卡,也称为千兆以太网卡,其传输速…

Java的list.forEach方法和foreach效率有区别吗

Java的list.forEach方法与传统的foreach循环在许多场景中都被广泛应用,它们的区别主要有:1、工作原理;2、性能比较;3、实际应用中的考量;4、使用场景的建议。foreach循环也是基于Iterator的,它提供了一种更加简洁的语法来迭代集合中的元素。1、工作原理 list.forEach方法…

LightningChart控件nuget版本

根据项目选择对应的版本:12 对应4.8 11对应 4.8 10.5对应 4.6 10.4 对应4.5技术娴熟,稳得一匹。

最好的 PHP 框架是什么

LARAVEL称为最佳PHP框架,因为它提供了出色的社区支持、广泛的功能以及优雅的语法。SYMFONY鉴于它的可重用组件和模块化,被赞誉具有高度的灵活性。CODEIGNITER因其轻量级和执行速度快而赢得开发者青睐。不过,选择最适合的框架还需考虑项目特定需求和团队熟悉度。 框架在现代W…

freeswitch的话单处理

概述 freeswitch是一款简单好用的VOIP开源软交换平台。 如果对cdr话单要求不高,可以直接使用fs的原始话单文件,使用脚本做一些简单的统计。 环境 CentOS 7.9 freeswitch 1.10.7 docker 话单配置 修改conf/autoload_configs/cdr_csv.conf.xml文件如下。 <param name="…

FaceRate.ai:精准的面部评分与深度分析,为你的外貌带来全新视角

FaceRate.ai是一款面部分析工具,通过精准评分和黄金比例测试,帮助用户深入了解自己的面部特征。它不仅适合个人提升外貌认知,还为美容师、设计师、艺术家等提供创作灵感。摘要:FaceRate.ai是一款面部分析工具,通过精准评分和黄金比例测试,帮助用户深入了解自己的面部特征…

Lossless Recompression of JPEG Images Using Transform Domain Intra Prediction

目录简介引入文章贡献对 DCT 系数残差进行编码比对原始 DCT 系数进行编码更有利于压缩模型框架模型的应用场景null 简介 \(\quad\)JPEG图像编码格式由于其简单高效在各种设备和网站上被广泛使用,随着互联网的发展和移动设备的普及大量用户生成的JPEG图像被上传到各社交网站或者…

国产!瑞芯微米尔RK357核心板革新AIoT设备,8核6T高算力

随着科技的快速发展,AIoT智能终端对嵌入式模块的末端计算能力、数据处理能力等要求日益提高。近日,米尔电子发布了一款基于瑞芯微RK3576核心板和开发板。核心板提供4GB/8GB LPDDR4X、32GB/64GB eMMC等多个型号供选择。瑞芯微RK3576核心优势主要包括高性能数据处理能力、领先的…

苹果的AirPods和其他品牌无线耳机有什么区别_1

苹果的AIrPods自推出以来就在无线耳机市场上引起了广泛关注,它们以其独特的设计、无缝的设备集成和优质的用户体验而著称。本文将探讨AirPods与其他品牌无线耳机的主要差异有:1.设计和舒适度;2.音质和性能;3.价格和价值;4.电池寿命和充电;5.兼容性和功能;6.附加功能;7.…

苹果M1芯片和Intel芯片在性能上有哪些差异

苹果M1芯片和Intel芯片的性能差异显著,主要体现在以下几个方面:1. 架构设计不同;2. 性能与效率平衡不同;3. 图形处理能力不同;4. AI和机器学习性能不同;5. 能耗和热管理不同;6. 兼容性和多任务处理不同。M1芯片作为苹果公司自研的首款ARM架构芯片,与Intel的x86架构芯片…

vs编译项目失败,提示 要求“SourceRoot”路径以斜杠或反斜杠结尾

vs编译项目失败,提示 要求“SourceRoot”路径以斜杠或反斜杠结尾处理办法从git上下载部分项目编译时出现错误,提示如下: 严重性 代码 说明 项目 文件 行 禁止显示状态 详细信息错误(活动) 要求“SourceRoot”路径以斜杠或反斜杠结尾:“E:\dev_tools\.nuget\packages” MahA…