spring集成guava的event bus

Guava的event bus

guava, https://github.com/google/guava 是一個很是有名的Java類庫,提供了不少在平常開發中經常使用的集合、函數接口等。此外,guava還提供了一個模塊叫作event bus,生產者往event bus上投遞消息,event bus負責回調訂閱了此類消息的回調函數,實現了消息生產者和消費者之間的解耦和異步處理。如下是一個簡單的例子:git

public class SimpleListener {
    @Subscribe
    public void task(String s) {
        System.out.println("do task(" + s + ")");
    }
}
public class SimpleEventBusExample {
    public static void main(String[] args) {
        EventBus eventBus = new EventBus();
        eventBus.register(new SimpleListener());
        System.out.println("Post Simple EventBus Example");
        eventBus.post("Simple EventBus Example");
    }
}

outputgithub

Post Simple EventBus Example
do task(Simple EventBus Example)

event bus集成到spring中

在以前的例子和guava的官方文檔裏面能夠看到,guava的event bus使用方式以下spring

1. 聲明一個event bus對象(線程安全,因此能夠作到全局惟一,並且訂閱者和發佈者必須共享這個event bus對象)安全

2. 對於訂閱者,支持 @Subscribe,定義處理消息的回調函數。併發

3. 對每個訂閱者,須要調用event bus的register方法,才能收到消息訂閱。app

對於1 和 2,都比較好。可是對於第3步來講,則有一點困難。由於框架

1. 在spring中,一般的你的訂閱者還會依賴其餘的spring管理的bean,因而你的訂閱者也會被歸入到spring的生命週期的管理中來,這樣若是用new的方式來初始化一個訂閱者,顯得很是的"不spring"。異步

2. 對於每一個訂閱者,都要顯式的註冊到event bus裏面,這樣並無作到關注點分離。理想的狀況下,訂閱者是不該該去關注如何註冊到event bus中。它只應該申明處理消息的回調函數,以及該回調函數是否可以併發調用。註冊到event bus中這件事,對於訂閱者應該是被動且自動的(只須要申明本身是否想註冊到event bus,而不須要關心細節)。這一點在多人開發,而且項目人員水平良莠不齊的時候,尤爲重要。ide

那麼如何作到自動註冊呢?其實答案很簡單,在spring中,ApplicationContext這個類提供了一系列的方法去獲取到當前spring context中的bean,只須要在event bus初始化以後,經過ApplicationContext來獲取當前有哪些訂閱者,而且主動的去註冊就行。因爲,guava的實現中,並無要求訂閱者實現某個接口,而是用註解的方式來聲明回調函數的,則這篇文章中的實現,也不須要訂閱者去實現某個接口,而是用註解的方式來申明本身是一個訂閱者。代碼以下函數

先聲明一個註解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Service
public @interface EventSubscriber {}

對於一個訂閱者,在類的接口上,加上這個註解,而且肯定這個bean會在歸入spring的生命週期管理中。

@EventSubscriber
public class SimpleSubscriber implements HiDasSubscriber {
@Autowired Somebean somebean; @Subscribe @AllowConcurrentEvents
public Integer logEventToDbAndUpdateStatus(String event) { System.out.println("receive a event:"+event); } }

聲明一個event bus的服務,而且在初始化以後,經過ApplicationContext的 getBeansWithAnnotation 的方法把全部的訂閱者獲取,而且註冊到event bus中。

@Service
public class EventBusService implements InitializingBean{

    private EventBus innerBus;

    @Inject
    private ApplicationContext appContext;

    public void unRegister(Object eventListener){
        innerBus.unregister(eventListener);
    }

    public void postEvent(String event){
        innerBus.post(event);
    }

    public void register(Object eventListener){
        innerBus.register(eventListener);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        innerBus = new AsyncEventBus("Hidas-event-bus", Executors.newCachedThreadPool());
        appContext.getBeansWithAnnotation(EventSubscriber.class).forEach((name, bean) -> {
            innerBus.register(bean);
        });
    }
}

完成了這兩步以後。若是其餘的消息生產者要往event bus上發消息,只須要注入這個event bus service,而且調用其post方法就行了。

注:這個只是例子,在實際項目中,對全部的消息都會聲明一個基類或者接口,每一個訂閱者對只會處理消息的某個具體實現。這樣event bus會根據消息的具體類型,來調用真正關注此類消息的訂閱者的回調函數。這樣比起讓全部消息訂閱者去實現一個 onEvent(BaseEvent event)的方法, 其實是避免了一個多路分配的問題。

總結

對於一些類庫在spring中使用,這種方法其實是一種通用的模式,實現某個接口或者編寫某個註解,而後經過ApplicationContext來獲取對應的bean,以後進行某些註冊或者組裝操做。這樣的話,可讓業務的代碼,和框架的代碼作到必定程度的關注點分離。

相關文章
相關標籤/搜索