文章連結:liuyueyi.github.io/hexblog/hex…
Spring之事件驅動機制的簡單使用
關於事件的發起與相應,在客戶端的互動中可算是非常頻繁的事情了,關於事件的釋出訂閱,在Java生態中,EventBus可謂是非常有名了,而Spring也提供了事件機制,本文則主要介紹後端如何在Spring的環境中,使用事件機制
I. 使用姿勢
主要藉助org.springframework.context.ApplicationEventPublisher#publishEvent(org.springframework.context.ApplicationEvent)
來發布事件,而接受方,則直接在處理的方法上,新增 @@EventListener
註解即可
1. 事件定義
釋出一個事件,所以第一件事就是要定義一個事件,對Spring而言,要求自定義的事件繼承自ApplicationEvent
類, 一個簡單的demo如下
public class NotifyEvent extends ApplicationEvent {
@Getter
private String msg;
public NotifyEvent(Object source, String msg) {
super(source);
this.msg = msg;
}
}
複製程式碼
2. 釋出事件
釋出時間則比較簡單,直接拿到ApplicationContext例項,執行publish方法即可,如下面給出一個簡單的釋出類
@Component
public class NotifyPublisher implements ApplicationContextAware {
private ApplicationContext apc;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.apc = applicationContext;
}
// 釋出一個訊息
public void publishEvent(int status, String msg) {
if (status == 0) {
apc.publishEvent(new NotifyEvent(this, msg));
} else {
apc.publishEvent(new NewNotifyEvent(this, msg, ((int) System.currentTimeMillis() / 1000)));
}
}
}
複製程式碼
3. 事件監聽器
在方法上新增註解即可,如下
@Component
public class NotifyQueueListener {
@EventListener
public void consumerA(NotifyEvent notifyEvent) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("A: " + Thread.currentThread().getName() + " | " + notifyEvent.getMsg());
}
@EventListener
public void consumerB(NewNotifyEvent notifyEvent) {
System.out.println("B: " + Thread.currentThread().getName() + " | " + notifyEvent.getMsg());
}
@EventListener
public void consumerC(NotifyEvent notifyEvent) {
System.out.println("C: " + Thread.currentThread().getName() + " | " + notifyEvent.getMsg());
}
}
複製程式碼
II. 疑問及解答
1. 釋出與監聽器的關聯
上面給出了使用的姿勢,看起來並不複雜,也比較容易使用,但是一個問題需要在使用之前弄明白了,釋出事件和監聽器是怎麼關聯起來的呢?
- 根據方法的引數型別執行
那麼如果釋出者,推送的是一個NotifyEvent
型別的事件,那麼接收者是怎樣的呢?
- 引數為NotifyEvent以及其子類的監聽器,都可以接收到訊息
測試用例如下:
NewNotifyEvent
繼承自上面的NotifyEvent
public class NewNotifyEvent extends NotifyEvent {
@Getter
private int version;
public NewNotifyEvent(Object source, String msg) {
super(source, msg);
}
public NewNotifyEvent(Object source, String msg, int version) {
super(source, msg);
this.version = version;
}
}
複製程式碼
然後藉助上面的訊息釋出者傳送一個訊息
@Test
public void testPublishEvent() throws InterruptedException {
notifyPublisher.publishEvent(1, "新的釋出事件! NewNotify");
System.out.println("---------");
notifyPublisher.publishEvent(0, "舊的釋出事件! Notify");
}
複製程式碼
輸出結果如下,對於NewNotifyEvent, 引數型別為NotifyEvent的consumerA, consumerC都可以接收到
A: main | 新的釋出事件! NewNotify
C: main | 新的釋出事件! NewNotify
B: main | 新的釋出事件! NewNotify
---------
A: main | 舊的釋出事件! Notify
C: main | 舊的釋出事件! Notify
複製程式碼
2. 訊息接收的順序
上面訊息處理是序列的,那麼先後順序怎麼確定? (下面的答案不確定,有待深入原始碼驗證!!!)
- 先掃描到的bean先處理
- 同一個bean中,按精確匹配,先後定義順序進行
3. 非同步消費
對於非同步消費,即在消費者方法上新增一個@Async
註解,並需要在配置檔案中,開啟非同步支援
@Async
@EventListener
public void processNewNotifyEvent(NewNotifyEvent newNotifyEvent) {
System.out.println("new notifyevent: " + newNotifyEvent.getMsg() + " : " + newNotifyEvent.getVersion());
}
複製程式碼
配置支援
@Configuration
@EnableAsync
public class AysncListenerConfig implements AsyncConfigurer {
/**
* 獲取非同步執行緒池執行物件
*
* @return
*/
@Override
public Executor getAsyncExecutor() {
return new ThreadPoolExecutor(5, 10, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<Runnable>(),
new DefaultThreadFactory("test"), new ThreadPoolExecutor.CallerRunsPolicy());
}
}
複製程式碼
III. 其他
一灰灰Blog: https://liuyueyi.github.io/hexblog
一灰灰的個人部落格,記錄所有學習和工作中的博文,歡迎大家前去逛逛
宣告
盡信書則不如,已上內容,純屬一家之言,因個人能力有限,難免有疏漏和錯誤之處,如發現bug或者有更好的建議,歡迎批評指正,不吝感激
- 微博地址: 小灰灰Blog
- QQ: 一灰灰/3302797840