前言
關於事件驅動模型,百度百科在有明確的解釋。在
JDK
的Util
包里抽象了事件驅動,有興趣的朋友可以自行去看下相關類的定義。Spring事件模型ApplicationEvent
是基於JDK
里的事件模型,廢話不多說,直接看Spring是如何定義事件模型,以及在具體業務場景中的應用。
事件
事件就是事件,鼠標點擊一下算一個事件,某個按鈕被點擊了一下算一個點擊事件,那么我訂單支付了可以認為支付也算一個件事!觸發了某個事件...... 等等。
抽象類ApplicationEvent
承載着我們要傳播的事件或者消息,白話就是說可以把某個對象用ApplicationEvent
這個對象來里的Source
來引用。
監聽者
上面我們定義了事件,那么事件產生的一系列的效應或者是變動,那么都由這些監聽者們去實現。點擊下鼠標(事件),那么我記錄下日志,你彈出個提示框。支付某個訂單(事件),我記錄下記錄,他發送個支付通知...... 等等。
泛型接口ApplicationListener
規定了泛型E的上邊界為ApplicationEvent
,意思很明確,就是給我們自定義事件用的。Spring
最大的優點我認為是留給用戶發揮的空間很大,就像神秘的海洋一樣,它一直有你探索不完的秘密,每一次你去了解它,它都能給你帶來新的事物和理解。
實戰
文章SpringPlugin-Core在業務中的應用中,我們用SpringPlugin
插件的方式去實現了訂單的不同操作!而在某個操作里面,我們可能又要發送操作事件的通知,比如:訂單支付了后,要通知打印機打印小票、微信公眾號提醒支付信息等等。那么我們來實際的操作下。
定義事件源
public class OrderPayedEvent extends ApplicationEvent {
/**
* 消息體,這里就設定為當前訂單對象
*/
private final Order order;
public OrderPayedEvent(Object source) {
super(source);
this.order = (Order) source;
}
public Order getOrder() {
return order;
}
}
實現ApplicationEvent
, 我這里Source
實際傳遞就是Order
對象,當然你也可以定義其他的多參數構造函數!
定義監聽者
定義監聽者的方式,Spring
提供了兩種,一種是接口方式,一種是注解方式。
接口方式
@Component
@Order(1)
public class OrderPayedPrinterListener implements ApplicationListener<OrderPayedEvent> {
@Override
public void onApplicationEvent(OrderPayedEvent event) {
System.out.printf("【線程 - %s 】訂單成功成功:第一步,打印小票%n", Thread.currentThread().getName());
}
}
@Component
@Order(2)
public class OrderPayedSendMessageListener implements ApplicationListener<OrderPayedEvent> {
@Override
public void onApplicationEvent(OrderPayedEvent event) {
System.out.printf("【線程 - %s 】訂單成功成功:第二步,發送通知商品中心添加庫存%n", Thread.currentThread().getName());
}
}
這里我定義了兩個監聽者,實現泛型接口ApplicationListener
類型為我們剛定義的OrderPayedEvent
這里加上Order
注解,是因為我們有多個監聽者,有此業務場景中可能會有順序的要求!
注解方式
@Component
public class OrderPayListener {
@EventListener(classes = {OrderPayedEvent.class})
public void sendTips(OrderPayedEvent event) {
System.out.printf("【線程 - %s 】訂單成功成功:發送用戶訂單支付消息%n", Thread.currentThread().getName());
}
@EventListener(classes = {OrderPayedEvent.class})
public void reward(OrderPayedEvent event) {
System.out.printf("【線程 - %s 】訂單成功成功:獎勵業務%n", Thread.currentThread().getName());
}
}
兩種方式,各有千秋,不同業務場景選擇不同實現方式即可。但注解方式是不會有排序功能的,如果你有業務有需要排序,那么建議換成接口方式
發布件事
萬事具備,只欠東風。那么只要合適的位置發布事件即可,那么在上回文章中的支付成功代碼加上事件即可
@Component
public class PayOperator implements OrderOperatorPlugin {
//這里注入 應用上下文,可以注入 applicationEventPublisher
@Resource
ApplicationContext context;
// @Resource
// ApplicationEventPublisher applicationEventPublisher;
@Override
public Optional<?> apply(OrderOperatorDTO operator) {
//支付操作
//doPay()
//發送事件
context.publishEvent(new OrderPayedEvent(new Order()));
return Optional.of("支付成功");
}
@Override
public boolean supports(OrderOperatorDTO operatorDTO) {
return operatorDTO.getOperatorType() == OrderOperatorType.PAY;
}
}
打印如下:
【線程 - main 】訂單成功成功:第一步,打印小票
【線程 - main 】訂單成功成功:第二步,發送通知商品中心添加庫存
【線程 - main 】訂單成功成功:第四步,獎勵業務
【線程 - main 】訂單成功成功:第三步,發送用戶訂單支付消息
那么,ApplicationEvent
對異步支持是怎么樣的呢?
只要在啟動類上加上@EnableAsync
,在方法體加上@Async
再打印如下:
【線程 - task-1 】訂單成功成功:第一步,打印小票
【線程 - task-2 】訂單成功成功:第二步,發送通知商品中心添加庫存
【線程 - task-4 】訂單成功成功:第四步,獎勵業務
【線程 - task-3 】訂單成功成功:第三步,發送用戶訂單支付消息
總結
不管是EventObject
,還是Observable
模型,都是用來解耦代碼。高內聚,低耦合的設計思想一至到現在都沒有被突破過,也是我們在日常工作過程中時刻要提醒自己的編碼思想。而我們更要利用好這些前人留下的精髓,應用到我們實際的業務場景中去。