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