天天看點

Google Guava:EventBus源碼解析和釋出訂閱消息的簡單使用示例

EventBus是Guava中對于事件釋出訂閱功能的實作,是設計模式中的釋出/訂閱模式的一種實作方案。

功能概括:

通過eventBus.register注冊訂閱者,通過eventBus.post方法釋出事件,然後根據釋出事件的類型(classType),執行所有訂閱者中被@Subcribe注解标記的且參數類型一緻的方法,進而實作釋出、訂閱功能。

源碼解讀:

源碼基于如下版本:

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>25.1-jre</version>
</dependency>
           

1. 通過eventBus.register()方法注冊訂閱者

Google Guava:EventBus源碼解析和釋出訂閱消息的簡單使用示例

2. 将該訂閱者注冊到該eventBus中,源碼見findAllSubcribers(listener)方法。在這個過程中,會掃描到被注冊類中被@Subcribe注解标記的方法,并将這些方法按照 入參類型 分類,以入參類型為key添加到Multimap<Class<?>, Subcriber>中,入參類型可以使用自定義對象。

Google Guava:EventBus源碼解析和釋出訂閱消息的簡單使用示例

3. 通過eventBus.post()方法釋出事件,根據事件類型找到所有對應subcriber,周遊;然後由eventBus中的線程池執行訂閱者中與入參類型比對的方法。

Google Guava:EventBus源碼解析和釋出訂閱消息的簡單使用示例
Google Guava:EventBus源碼解析和釋出訂閱消息的簡單使用示例

簡單使用示例:

在springboot中,使用自動注入的方式來實作

目錄結構:

Google Guava:EventBus源碼解析和釋出訂閱消息的簡單使用示例

代碼如下:

package com.example.demo.eventbus.config;

import com.google.common.eventbus.EventBus;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 注入eventBus bean
 *
 */
@Configuration
public class EventBusConfig {

    @Bean("eventBus")
    public EventBus eventBus() {
        return new EventBus("demo");
    }
}


package com.example.demo.eventbus.listener;

import com.google.common.eventbus.EventBus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.Map;

/**
 * 注冊eventListener
 *
 */
@Component
public class ApplicationListenerRegister implements ApplicationListener<ContextRefreshedEvent> {

    @Autowired
    private EventBus eventBus;

    @Autowired
    private ConfigurableApplicationContext context;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        Map<String, IEventListener> listenerMap = context.getBeansOfType(IEventListener.class);
        if (!CollectionUtils.isEmpty(listenerMap)) {
            for (Map.Entry<String, IEventListener> entry : listenerMap.entrySet()) {
                eventBus.register(entry.getValue());
                System.out.println("eventListener registed: " + entry.getValue().getClass());
            }
        }
    }
}


package com.example.demo.eventbus.listener;

/**
 * 事件監聽者接口
 *
 */
public interface IEventListener {
}


package com.example.demo.eventbus.listener;

import com.google.common.eventbus.Subscribe;
import org.springframework.stereotype.Component;

/**
 * 監聽Integer
 *
 */
@Component
public class ListenerInt implements IEventListener{

    @Subscribe
    public int call(Integer num) {
        System.out.println("ListenerInt call(): " + num);
        return num;
    }

}


package com.example.demo.eventbus.listener;

import com.google.common.eventbus.Subscribe;
import org.springframework.stereotype.Component;

/**
 * 監聽字元串事件
 *
 */
@Component
public class ListenerStr implements IEventListener{

    @Subscribe
    public String call(String str) {
        System.out.println("ListenerStr call() : " + str);
        return str;
    }

    @Subscribe
    public String call2(String str) {
        System.out.println("ListenerStr call2() : " + str);
        return str;
    }
}


package com.example.demo.eventbus;

import com.google.common.eventbus.EventBus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * eventBus測試
 *
 */
@RestController
@RequestMapping("/event")
public class EventBusTestController {


    @Autowired
    private EventBus eventBus;

    @GetMapping("/eventTest")
    public void eventTest() {
        System.out.println("eventTest started");
        eventBus.post("string test");

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        eventBus.post(111);
        System.out.println("eventTest end");
    }
}

           

運作結果:

Google Guava:EventBus源碼解析和釋出訂閱消息的簡單使用示例

補充:

别的部落格中看到過這樣一段總結,感覺不錯,摘錄下:(出自:https://blog.csdn.net/yanghua_kobe/article/details/46317297)

總結

Guava的EventBus源碼還是比較簡單、清晰的。從源碼來看,它一番常用的Observer的設計方式,放棄采用統一的接口、統一的事件對象類型。轉而采用基于注解掃描的綁定方式。

其實無論是強制實作統一的接口,還是基于注解的實作方式都是在建構一種關聯關系(或者說滿足某種契約)。很明顯接口的方式是編譯層面上強制的顯式契約,而注解的方式則是運作時動态綁定的隐式契約關系。接口的方式是傳統的方式,編譯時确定觀察者關系,清晰明了,但通常要求有一緻的事件類型、方法簽名。而基于注解實作的機制,剛好相反,編譯時因為沒有接口的文法層面上的依賴關系,顯得不那麼清晰,至少靜态分析工具很難展示觀察者關系,但無需一緻的方法簽名、事件參數,至于多個訂閱者類之間的繼承關系,可以繼承接收事件的通知,可以看作既是其優點也是其缺點。