前言
監(jiān)聽器: 當(dāng)某個(gè)事件觸發(fā)的時(shí)候,就會(huì)執(zhí)行的方法塊。
springboot提供了兩個(gè)接口來實(shí)現(xiàn)監(jiān)聽:ApplicationListener、SmartApplicationListener,如下圖。顯而易見,SmartApplicationListener 是 ApplicationListener 的子類,故而其功能要強(qiáng)于 ApplicationListener。
當(dāng)然,springboot很貼心地提供了一個(gè) @EventListener 注解來實(shí)現(xiàn)監(jiān)聽。
1. ApplicationListener
1. 簡單的全局監(jiān)聽
首先,先來簡單體驗(yàn)一下監(jiān)聽器的功能。
需求: 在spring容器初始化完成之后就開始監(jiān)聽,并打印日志。
實(shí)現(xiàn):
-
準(zhǔn)備springboot工程(依賴:springboot、lombok)
-
寫一個(gè)監(jiān)聽器
@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("我已經(jīng)監(jiān)聽到了"); } } }
-
啟動(dòng)項(xiàng)目,控制臺(tái)輸出如下:
現(xiàn)在來說一下為什么要這么寫監(jiān)聽器:
-
實(shí)現(xiàn)接口
implements ApplicationListener<ContextRefreshedEvent>
自定義監(jiān)聽器需要實(shí)現(xiàn) ApplicationListener<E extends ApplicationEvent> 接口
ContextRefreshedEvent 是一個(gè)事件,它會(huì)在 spring容器初始化完成 之后被觸發(fā),所以監(jiān)聽器就會(huì)在 spring容器初始化完成之后開始監(jiān)聽,所以這就是所謂的全局監(jiān)聽 -
標(biāo)志位 aFlag
private static boolean aFlag = false;
aFlag 是一個(gè)啟動(dòng)標(biāo)志
因?yàn)閣eb應(yīng)用會(huì)出現(xiàn)父子容器,這樣就會(huì)觸發(fā)兩次監(jiān)聽任務(wù),所以需要一個(gè)標(biāo)志位,保證監(jiān)聽任務(wù)(log.info(“我已經(jīng)監(jiān)聽到了”))只會(huì)觸發(fā)一次
2. 定時(shí)任務(wù)
需求: 實(shí)現(xiàn)一個(gè)定時(shí)任務(wù),每間隔5秒、10秒、15秒、20秒、25秒、30秒、40秒、50秒、60秒,在控制臺(tái)打印一次日志。
實(shí)現(xiàn): 可通過多線程的方式實(shí)現(xiàn)
-
新建一個(gè)類 TimerRunner 繼承 Runnable
-
5秒到60秒的間隔可以通過 枚舉類實(shí)現(xiàn)
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() { // 打印次數(shù) int count = 1; SimpleDateFormat dateFormat= new SimpleDateFormat("hh:mm:ss"); for (TimerEnum item: TimerEnum.values()) { if (count == item.getCount()) { if (count != 9) { log.info("時(shí)間: " + dateFormat.format(new Date()) + "第 " + count + " 次打印,還剩余 " + (9 - count) + " 次完成打印"); count++; } else { log.info("最后一次打印"); log.info("已完成所有打印任務(wù)!"); } } 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(); } } }
-
控制臺(tái)輸出:
3. 監(jiān)聽自定義事件
Spring的
ApplicationContext
提供了支持事件和代碼中監(jiān)聽器的功能。
我們可以創(chuàng)建bean用來監(jiān)聽在ApplicationContext
中發(fā)布的事件。ApplicationEvent
類在ApplicationContext
接口中處理的事件,如果一個(gè)bean實(shí)現(xiàn)了ApplicationListener
接口,當(dāng)一個(gè)ApplicationEvent
被發(fā)布以后,bean會(huì)自動(dòng)被通知。
參考鏈接:https://cloud.tencent.com/developer/article/1532994

先來看一下 spring的內(nèi)置事件 :
內(nèi)置事件: 參考鏈接: https://blog.csdn.net/liyantianmin/article/details/81017960
事件 | 說明 |
---|---|
ContextRefreshedEvent | ApplicationContext 被初始化或刷新時(shí),該事件被發(fā)布。這也可以在 ConfigurableApplicationContext接口中使用 refresh() 方法來發(fā)生。此處的初始化是指:所有的Bean被成功裝載,后處理Bean被檢測并激活,所有Singleton Bean 被預(yù)實(shí)例化,ApplicationContext容器已就緒可用。 |
ContextStartedEvent | 當(dāng)使用 ConfigurableApplicationContext (ApplicationContext子接口)接口中的 start() 方法啟動(dòng) ApplicationContext 時(shí),該事件被發(fā)布。你可以調(diào)查你的數(shù)據(jù)庫,或者你可以在接受到這個(gè)事件后重啟任何停止的應(yīng)用程序。 |
ContextStoppedEvent | 當(dāng)使用 ConfigurableApplicationContext 接口中的 stop() 停止 ApplicationContext 時(shí),發(fā)布這個(gè)事件。你可以在接受到這個(gè)事件后做必要的清理的工作。 |
ContextClosedEvent | 當(dāng)使用 ConfigurableApplicationContext 接口中的 close() 方法關(guān)閉 ApplicationContext 時(shí),該事件被發(fā)布。一個(gè)已關(guān)閉的上下文到達(dá)生命周期末端;它不能被刷新或重啟。 |
RequestHandledEvent | 這是一個(gè) web-specific 事件,告訴所有 bean HTTP 請求已經(jīng)被服務(wù)。只能應(yīng)用于使用DispatcherServlet的Web應(yīng)用。在使用Spring作為前端的MVC控制器時(shí),當(dāng)Spring處理用戶請求結(jié)束后,系統(tǒng)會(huì)自動(dòng)觸發(fā)該事件。 |
自定義監(jiān)聽事件:
-
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; } }
-
監(jiān)聽器
@Slf4j @Component public class MyTask implements ApplicationListener { private static boolean aFlag = false; @Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ContextRefreshedEvent) { log.info("監(jiān)聽到 ContextRefreshedEvent..."); } if (event instanceof MyEvent) { log.info("監(jiān)聽到 MyEvent..."); MyEvent myEvent = (MyEvent) event; System.out.println("時(shí)間:" + myEvent.getTime() + " 信息:" + myEvent.getMsg()); } } }
-
觸發(fā)事件
自定義監(jiān)聽事件需要主動(dòng)觸發(fā)@SpringBootApplication public class TaskApplication { public static void main(String[] args) { ConfigurableApplicationContext run = SpringApplication.run(TaskApplication.class, args); MyEvent event = new MyEvent("event", "忙中歲月忙中遣,我本愚來性不移"); // 發(fā)布事件 run.publishEvent(event); } }
也可以這樣觸發(fā),美觀一點(diǎn)
@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", "忙中歲月忙中遣,我本愚來性不移"); // 發(fā)布事件 applicationContext.publishEvent(event); } }
-
控制臺(tái)輸出
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("監(jiān)聽到 ContextRefreshedEvent...");
}
if (event instanceof MyEvent) {
log.info("監(jiān)聽到 MyEvent...");
MyEvent myEvent = (MyEvent) event;
System.out.println("時(shí)間:" + 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 | 確認(rèn)當(dāng)前監(jiān)聽器是否支持當(dāng)前事件類型。 |
supportsSourceType | 確定此監(jiān)聽器是否實(shí)際支持給定的源類型。 |
getOrder | 確定此偵聽器在同一事件的一組偵聽器中的順序。數(shù)值越小,優(yōu)先級(jí)越高。 |
getListenerId | 返回偵聽器的可選標(biāo)識(shí)符。 |
3. @EventListener
使用:文章來源:http://www.zghlxwxcb.cn/news/detail-791580.html
@Slf4j
@Component
public class MyTask {
@EventListener
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextRefreshedEvent) {
log.info("監(jiān)聽到 ContextRefreshedEvent...");
}
if (event instanceof MyEvent) {
log.info("監(jiān)聽到 MyEvent...");
MyEvent myEvent = (MyEvent) event;
System.out.println("時(shí)間:" + myEvent.getTime() + " 信息:" + myEvent.getMsg());
}
}
}
@Slf4j
@Component
public class MyTask {
@EventListener
public void MyEventListener(MyEvent event) {
log.info("監(jiān)聽到 MyEvent...");
MyEvent myEvent = (MyEvent) event;
System.out.println("時(shí)間:" + myEvent.getTime() + " 信息:" + myEvent.getMsg());
}
@EventListener
public void ContextRefreshedEventListener(MyEvent event) {
log.info("監(jiān)聽到 ContextRefreshedEvent...");
}
}
指定監(jiān)聽事件的類型:文章來源地址http://www.zghlxwxcb.cn/news/detail-791580.html
@EventListener(MyEvent.class)
@EventListener({MyEvent.class, ContextRefreshedEvent.class})
到了這里,關(guān)于springboot監(jiān)聽器的使用(ApplicationListener、SmartApplicationListener、@EventListener)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!