天天看點

Guava的EventBus事件機制實作

作者:程式猿阿嘴

EventBus說明

EventBus是Guava封裝的事件處理機制,屬于設計模式中的觀察者模式(生産|消費者程式設計模型)。使用簡單、優雅隻關注業務本身,僅限JVM内部使用,缺點:不适用分布式,看實際業務可選用MQ。

Guava的EventBus事件機制實作
事件總線(EventBus)處理個服務、或業務間公共線路,而EventBus則是存儲事件、處理時間中間服務,通過post()往EventBus發送事件,由EventBus去排程對應訂閱方(subscriber)消費處理,解耦觀察者模式中訂閱方與事件源之間強依賴關系。

如何使用

  1. 引入Maven依賴
<dependency>
  <groupId>com.google.guava</groupId>
  <artifactId>guava</artifactId>
  <version>20.0</version>
</dependency>
複制代碼           
  1. 場景舉例(僞代碼)
public class EventBusCenter {
    private static EventBus eventBus = new EventBus();
    private EventBusCenter() {
    }
    public static EventBus getInstance(){
        return eventBus;
    }
    /**注冊監聽*/
    public static void register(Object listener){
        eventBus.register(listener);
    }
    /**取消注冊*/
    public static void unregister(Object listener){
        eventBus.unregister(listener);
    }
    /**釋出事件*/
    public static void post(Object event){
        eventBus.post(event);
    }
}
複制代碼           

定義業務對象

public class OrderInfo {
    private long orderId;
    private String orderName;
 }
 
 public class OrderFailInfo{
    private long orderId;
    private String orderName;
    private String msg;
}

複制代碼           

支付成功監聽處理事件

public class OrderSuccessListener {
    @Subscribe
    public void orderSubscribe(OrderInfo event) {
        // 訂單業務監聽
        System.out.println("訂單支付成功處理事件: " + event);
    }
    @Subscribe
    public void ortherSubscribe(OrderInfo event) {
        // 其它業務監聽
        System.out.println("訂單支付成功其它處理事件: " + event);
    }
}
複制代碼           

支付失敗監聽處理事件

public class OrderFailListener {
    @Subscribe
    public void orderSubscribe(OrderFailInfo event) {
        // 訂單業務監聽
        System.err.println("訂單支付失敗處理事件: " + event);
    }
    @Subscribe
    public void ortherSubscribe(String event) {
        // 其它業務監聽
        System.err.println("訂單支付失敗其它處理事件: " + event);
    }
}
複制代碼           

測試

public class EventBusTest {
    public static void main(String[] args) {
        OrderSuccessListener successListener = new OrderSuccessListener();
        OrderFailListener failListener = new OrderFailListener();
            EventBusCenter.register(successListener);
            EventBusCenter.register(failListener);
            OrderInfo order = OrderInfo.builder().orderId(111L).orderName("訂單名稱xxx").build();
            log.info("=====支付成功=======");
            EventBusCenter.post(order);

            log.info("=====支付失敗=======");
            OrderFailInfo orderFail = OrderFailInfo.builder().orderId(111L).orderName("訂單名稱xxx").msg("支付失敗").build();
            EventBusCenter.post(orderFail);
            log.info("=====記錄支付失敗原因=======");
            EventBusCenter.post("使用者取消訂單");
    }
}

輸出結果:
資訊: =====支付成功=======
訂單支付成功其它處理事件: OrderInfo(orderId=111, orderName=訂單名稱xxx)
訂單支付成功處理事件: OrderInfo(orderId=111, orderName=訂單名稱xxx)

資訊: =====支付失敗=======
訂單支付失敗處理事件: OrderFailInfo(orderId=111, orderName=訂單名稱xxx, msg=支付失敗)

資訊: =====記錄支付失敗原因=======
訂單支付失敗其它處理事件: 使用者取消訂單
複制代碼           

使用說明:

  • 隻有通過***@Subscribe***注解才會被注冊進EventBus
  • 監聽方法隻能有1個參數,當有多個監聽方法時,參數類型一樣并行消費;不同參類型對應不對消費事件

源碼分析

初始化EvenBus
EventBus(String identifier, Executor executor, Dispatcher dispatcher, SubscriberExceptionHandler exceptionHandler) {
        this.subscribers = new SubscriberRegistry(this);//所有觀察者清單維護對象
        this.identifier = (String) Preconditions.checkNotNull(identifier);// EventBus的處理辨別符(方法名稱)
        //executor是事件分發過程中使用到的線程池,可以自己實作
        this.executor = (Executor)Preconditions.checkNotNull(executor);
        //Dispatcher類型的子類,對監聽者分發政策,主要有3種方式
        this.dispatcher = (Dispatcher)Preconditions.checkNotNull(dispatcher);
        //異常處理策咯
        this.exceptionHandler = (SubscriberExceptionHandler)Preconditions.checkNotNull(exceptionHandler);
    }
複制代碼           
注冊監聽–>register()

作用:初始化監聽者類型與監聽方法的集合subscribers,在釋出事件場景由傳入參數類型,比對、并執行對應監聽者方法

Guava的EventBus事件機制實作

①擷取指定監聽者對應的全部觀察者集合(一對多)

②擷取對應**@Subscribe**觀察者事件類型(即:方法參數類型)集合

③添加對應類型所有觀察者到集合事件集合中(即:SubscriberRegistry對象維護的subscribers集合)

subscribers說明:

private final ConcurrentMap<Class<?>, CopyOnWriteArraySet<Subscriber>> subscribers = Maps.newConcurrentMap();
複制代碼           

防止并發問題維護一個ConcurrentMap集合,Subscriber集合對應velue使用的是Java中的CopyOnWriteArraySet集合,

主要作用:1、避免監聽重複事件 2、适用于讀多寫少的場景:隻要register()才寫入,基本都是查詢

findAllSubscribers()說明

Guava的EventBus事件機制實作

①擷取注冊監聽對象所有包含**@Subscribe**的方法(如下圖所示⤵️)

②通過反射擷取對應方法、及監聽參數類型

③放入目前初始化的EventBus對象裡面,維護監聽對象與監聽方法的對應關系

Guava的EventBus事件機制實作
Guava的EventBus事件機制實作

首先從subscriberMethodsCache緩存中擷取監聽對象映射關系,如果緩存中不存在通過反射周遊所有包含**@Subscribe**的方法

釋出事件–>post()
Guava的EventBus事件機制實作

①getSubscribers()方法擷取該事件對應的全部觀察者

②Dispatcher-分發事件政策

ImmediateDispatcher:直接在目前線程中周遊所有的觀察者并進行事件分發

LegacyAsyncDispatcher:使用全局隊列,先将觀察者依次放入隊列,再順序從隊列中取出觀察者對象進行事件分發

PerThreadQueuedDispatcher(預設):使用線程相關隊列,會先擷取目前線程的觀察者隊列,并将傳入的觀察者清單傳入到該隊列中;判斷目前線程是否正在進行分發操作,如果沒有在進行分發操作,就通過周遊上述隊列進行事件分發

最後無論使用哪個分發器,都會執行dispatchEvent()方法,通過反射(target、method、executor)多線程觸發監聽方法

Guava的EventBus事件機制實作
原文連結:https://juejin.cn/post/7224310314807083065

繼續閱讀