Spring事件,ApplicationEvent在業務中的應用


前言

關於事件驅動模型,百度百科在有明確的解釋。在JDKUtil包里抽象了事件驅動,有興趣的朋友可以自行去看下相關類的定義。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模型,都是用來解耦代碼。高內聚,低耦合的設計思想一至到現在都沒有被突破過,也是我們在日常工作過程中時刻要提醒自己的編碼思想。而我們更要利用好這些前人留下的精髓,應用到我們實際的業務場景中去。

代碼在GitHub


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM