詳解SpringBoot實現(xiàn)事件同步與異步監(jiān)聽
簡介
說明
本文用示例介紹SpringBoot中的事件的用法及原理。
事件監(jiān)聽簡述
事件的發(fā)布與監(jiān)聽從屬于觀察者模式;和MQ相比,事件的發(fā)布與監(jiān)聽偏向于處理服務(wù)內(nèi)的某些邏輯。
多個監(jiān)聽器可以監(jiān)聽同一個事件。例如:發(fā)布了事件A,監(jiān)聽器A和監(jiān)聽器B都監(jiān)聽了事件A,則監(jiān)聽器A和B都會進行處理。
同步與異步監(jiān)聽
| 監(jiān)聽方式 | 特點 | 使用時機 |
|---|---|---|
| 同步監(jiān)聽 | 發(fā)布器線程與監(jiān)聽器線程處于同一線程 | 1.監(jiān)聽邏輯處理較快 2.需要緊接著根據(jù)監(jiān)聽器追蹤業(yè)務(wù)線程 |
| 異步監(jiān)聽 | 發(fā)布器線程與監(jiān)聽器線程處于不同線程 | 1.監(jiān)聽邏輯處理比較耗時 2.追求響應(yīng)性能 |
事件的順序
可使用實現(xiàn)Ordered接口的方式,調(diào)整監(jiān)聽器順序。
注意:必須是同時實現(xiàn) ApplicationListener<MyEvent>,Ordered這樣的方法才能控制順序。
下邊幾種都是無法控制順序的:
- @Component+@EventListerner+實現(xiàn)Ordered
- 實現(xiàn) ApplicationListener<MyEvent>+@Order
實例
同步監(jiān)聽(無序)
事件
package com.example.event;
import org.springframework.context.ApplicationEvent;
public class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
super(source);
}
}
監(jiān)聽器
監(jiān)聽器1
package com.example.event;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class MyListener {
@EventListener
public void abc(MyEvent event) {
System.out.println("監(jiān)聽器: " + "MyListener");
System.out.println("監(jiān)聽器所在線程:" + Thread.currentThread().getName());
System.out.println("事件: " + event);
System.out.println("事件的數(shù)據(jù): " + event.getSource());
}
}
監(jiān)聽器2
package com.example.event;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class MyListener2 {
@EventListener
public void onApplicationEvent(MyEvent event) {
System.out.println("監(jiān)聽器: " + "MyListener2");
System.out.println(" 所在線程: " + Thread.currentThread().getName());
System.out.println(" 事件: " + event);
System.out.println(" 事件的數(shù)據(jù):" + event.getSource());
}
}
發(fā)布器
package com.example.event;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
@Component
public class MyPublisher {
@Autowired
ApplicationContext applicationContext;
public void myPublish(String message) {
System.out.println("發(fā)布器所在線程:" + Thread.currentThread().getName());
applicationContext.publishEvent(new MyEvent(message));
}
}
測試
寫一個Controller
package com.example.controller;
import com.example.event.MyPublisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@Autowired
MyPublisher myPublisher;
@GetMapping("/test1")
public String test1() {
myPublisher.myPublish("Hello");
return "test1 success";
}
}
啟動后,訪問:http://localhost:8080/test1
后端輸出:
發(fā)布器所在線程:http-nio-8080-exec-1
監(jiān)聽器: MyListener
所在線程: http-nio-8080-exec-1
事件: com.example.event.MyEvent[source=Hello]
事件的數(shù)據(jù):Hello
監(jiān)聽器: MyListener2
所在線程: http-nio-8080-exec-1
事件: com.example.event.MyEvent[source=Hello]
事件的數(shù)據(jù):Hello
可以發(fā)現(xiàn),所有監(jiān)聽器和發(fā)布器都在同一個線程。
同步監(jiān)聽(有序)
事件
package com.example.event;
import org.springframework.context.ApplicationEvent;
public class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
super(source);
}
}
監(jiān)聽器
監(jiān)聽器1
package com.example.event;
import org.springframework.context.ApplicationListener;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
@Component
public class MyListener implements ApplicationListener<MyEvent>, Ordered {
@Override
public void onApplicationEvent(MyEvent event) {
System.out.println("監(jiān)聽器: " + "MyListener");
System.out.println(" 所在線程: " + Thread.currentThread().getName());
System.out.println(" 事件: " + event);
System.out.println(" 事件的數(shù)據(jù):" + event.getSource());
}
@Override
public int getOrder() {
return 2;
}
}
監(jiān)聽器2
package com.example.event;
import org.springframework.context.ApplicationListener;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
@Component
public class MyListener2 implements ApplicationListener<MyEvent>, Ordered {
public void onApplicationEvent(MyEvent event) {
System.out.println("監(jiān)聽器: " + "MyListener2");
System.out.println(" 所在線程: " + Thread.currentThread().getName());
System.out.println(" 事件: " + event);
System.out.println(" 事件的數(shù)據(jù):" + event.getSource());
}
@Override
public int getOrder() {
return 1;
}
}
發(fā)布器
package com.example.event;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
@Component
public class MyPublisher {
@Autowired
ApplicationContext applicationContext;
public void myPublish(String message) {
System.out.println("發(fā)布器所在線程:" + Thread.currentThread().getName());
applicationContext.publishEvent(new MyEvent(message));
}
}
測試
寫一個Controller
package com.example.controller;
import com.example.event.MyPublisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@Autowired
MyPublisher myPublisher;
@GetMapping("/test1")
public String test1() {
myPublisher.myPublish("Hello");
return "test1 success";
}
}
啟動后,訪問:http://localhost:8080/test1
后端輸出:
發(fā)布器所在線程:http-nio-8080-exec-1
監(jiān)聽器: MyListener2
所在線程: http-nio-8080-exec-1
事件: com.example.event.MyEvent[source=Hello]
事件的數(shù)據(jù):Hello
監(jiān)聽器: MyListener
所在線程: http-nio-8080-exec-1
事件: com.example.event.MyEvent[source=Hello]
事件的數(shù)據(jù):Hello
如果將監(jiān)聽器的實現(xiàn)的Ordered順序顛倒,則輸出結(jié)果如下:
發(fā)布器所在線程:http-nio-8080-exec-1
監(jiān)聽器: MyListener
所在線程: http-nio-8080-exec-1
事件: com.example.event.MyEvent[source=Hello]
事件的數(shù)據(jù):Hello
監(jiān)聽器: MyListener2
所在線程: http-nio-8080-exec-1
事件: com.example.event.MyEvent[source=Hello]
事件的數(shù)據(jù):Hello
異步監(jiān)聽(無序)
方法:
- 開啟異步監(jiān)聽
- 在監(jiān)聽器上加@Async(此監(jiān)聽器必須是@Component方法注冊的)
事件
package com.example.event;
import org.springframework.context.ApplicationEvent;
public class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
super(source);
}
}
同步監(jiān)聽器
package com.example.event;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class MyListener {
@EventListener
public void abc(MyEvent event) {
System.out.println("監(jiān)聽器: " + "MyListener");
System.out.println(" 所在線程: " + Thread.currentThread().getName());
System.out.println(" 事件: " + event);
System.out.println(" 事件的數(shù)據(jù):" + event.getSource());
}
}
異步監(jiān)聽器
package com.example.event;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
@Async
public class MyListener2 {
@EventListener
public void onApplicationEvent(MyEvent event) {
System.out.println("監(jiān)聽器: " + "MyListener2");
System.out.println(" 所在線程: " + Thread.currentThread().getName());
System.out.println(" 事件: " + event);
System.out.println(" 事件的數(shù)據(jù):" + event.getSource());
}
}
發(fā)布器
package com.example.event;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
@Component
public class MyPublisher {
@Autowired
ApplicationContext applicationContext;
public void myPublish(String message) {
System.out.println("發(fā)布器所在線程:" + Thread.currentThread().getName());
applicationContext.publishEvent(new MyEvent(message));
}
}
啟動類
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
測試
寫一個Controller
package com.example.controller;
import com.example.event.MyPublisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@Autowired
MyPublisher myPublisher;
@GetMapping("/test1")
public String test1() {
myPublisher.myPublish("Hello");
return "test1 success";
}
}
啟動后,訪問:http://localhost:8080/test1
后端輸出:
發(fā)布器所在線程:http-nio-8080-exec-1
監(jiān)聽器: MyListener
所在線程: http-nio-8080-exec-1
事件: com.example.event.MyEvent[source=Hello]
事件的數(shù)據(jù):Hello
監(jiān)聽器: MyListener2
所在線程: task-1
事件: com.example.event.MyEvent[source=Hello]
事件的數(shù)據(jù):Hello
到此這篇關(guān)于詳解SpringBoot實現(xiàn)同步與異步事件監(jiān)聽的文章就介紹到這了,更多相關(guān)SpringBoot事件監(jiān)聽內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
jenkins配置詳細指南(附j(luò)dk多個版本配置)
Jenkins是一款CICD(持續(xù)集成與持續(xù)交付)工具,Jenkins可以幫你在寫完代碼后,一鍵完成開發(fā)過程中的一系列自動化部署的工作,這篇文章主要給大家介紹了關(guān)于jenkins配置的相關(guān)資料,文中還附j(luò)dk多個版本配置指南,需要的朋友可以參考下2024-02-02
AsyncHttpClient?ClientStats源碼流程解讀
這篇文章主要為大家介紹了AsyncHttpClient?ClientStats源碼流程解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-12-12
Java?Web項目中解決中文亂碼方法總結(jié)(三種最新方法)
這篇文章主要介紹了Java?Web項目中解決中文亂碼方法總結(jié),本文給大家分享三種最新解決方法,需要的朋友可以參考下2022-06-06
談?wù)劄镴AXB和response設(shè)置編碼,解決wechat4j中文亂碼的問題
中文亂碼是每個程序員都會遇到的問題,本篇文章主要介紹了談?wù)劄镴AXB和response設(shè)置編碼,解決wechat4j中文亂碼的問題,具有一定的參考價值,有興趣的可以了解一下。2016-12-12
Java+WebSocket實現(xiàn)簡單實時雙人協(xié)同pk答題系統(tǒng)
在實時互動應(yīng)用中,實現(xiàn)流暢的多人協(xié)同對戰(zhàn)功能是一大挑戰(zhàn),WebSocket技術(shù),以其全雙工通信能力,提供了解決方案,本文我們就來使用WebSocket實現(xiàn)簡單實時雙人協(xié)同pk答題系統(tǒng)吧2025-06-06

