目录
- 前言
- 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; @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
事件 | 说明 |
---|---|
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; @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})