EventBus說明
EventBus是Guava封裝的事件處理機制,屬于設計模式中的觀察者模式(生産|消費者程式設計模型)。使用簡單、優雅隻關注業務本身,僅限JVM内部使用,缺點:不适用分布式,看實際業務可選用MQ。
事件總線(EventBus)處理個服務、或業務間公共線路,而EventBus則是存儲事件、處理時間中間服務,通過post()往EventBus發送事件,由EventBus去排程對應訂閱方(subscriber)消費處理,解耦觀察者模式中訂閱方與事件源之間強依賴關系。
如何使用
- 引入Maven依賴
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>
複制代碼
- 場景舉例(僞代碼)
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,在釋出事件場景由傳入參數類型,比對、并執行對應監聽者方法
①擷取指定監聽者對應的全部觀察者集合(一對多)
②擷取對應**@Subscribe**觀察者事件類型(即:方法參數類型)集合
③添加對應類型所有觀察者到集合事件集合中(即:SubscriberRegistry對象維護的subscribers集合)
subscribers說明:
private final ConcurrentMap<Class<?>, CopyOnWriteArraySet<Subscriber>> subscribers = Maps.newConcurrentMap();
複制代碼
防止并發問題維護一個ConcurrentMap集合,Subscriber集合對應velue使用的是Java中的CopyOnWriteArraySet集合,
主要作用:1、避免監聽重複事件 2、适用于讀多寫少的場景:隻要register()才寫入,基本都是查詢
findAllSubscribers()說明
①擷取注冊監聽對象所有包含**@Subscribe**的方法(如下圖所示⤵️)
②通過反射擷取對應方法、及監聽參數類型
③放入目前初始化的EventBus對象裡面,維護監聽對象與監聽方法的對應關系
首先從subscriberMethodsCache緩存中擷取監聽對象映射關系,如果緩存中不存在通過反射周遊所有包含**@Subscribe**的方法
釋出事件–>post()
①getSubscribers()方法擷取該事件對應的全部觀察者
②Dispatcher-分發事件政策
ImmediateDispatcher:直接在目前線程中周遊所有的觀察者并進行事件分發
LegacyAsyncDispatcher:使用全局隊列,先将觀察者依次放入隊列,再順序從隊列中取出觀察者對象進行事件分發
PerThreadQueuedDispatcher(預設):使用線程相關隊列,會先擷取目前線程的觀察者隊列,并将傳入的觀察者清單傳入到該隊列中;判斷目前線程是否正在進行分發操作,如果沒有在進行分發操作,就通過周遊上述隊列進行事件分發
最後無論使用哪個分發器,都會執行dispatchEvent()方法,通過反射(target、method、executor)多線程觸發監聽方法
原文連結:https://juejin.cn/post/7224310314807083065