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()方法注冊訂閱者
2. 将該訂閱者注冊到該eventBus中,源碼見findAllSubcribers(listener)方法。在這個過程中,會掃描到被注冊類中被@Subcribe注解标記的方法,并将這些方法按照 入參類型 分類,以入參類型為key添加到Multimap<Class<?>, Subcriber>中,入參類型可以使用自定義對象。
3. 通過eventBus.post()方法釋出事件,根據事件類型找到所有對應subcriber,周遊;然後由eventBus中的線程池執行訂閱者中與入參類型比對的方法。
簡單使用示例:
在springboot中,使用自動注入的方式來實作
目錄結構:
代碼如下:
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");
}
}
運作結果:
補充:
别的部落格中看到過這樣一段總結,感覺不錯,摘錄下:(出自:https://blog.csdn.net/yanghua_kobe/article/details/46317297)
總結
Guava的EventBus源碼還是比較簡單、清晰的。從源碼來看,它一番常用的Observer的設計方式,放棄采用統一的接口、統一的事件對象類型。轉而采用基于注解掃描的綁定方式。
其實無論是強制實作統一的接口,還是基于注解的實作方式都是在建構一種關聯關系(或者說滿足某種契約)。很明顯接口的方式是編譯層面上強制的顯式契約,而注解的方式則是運作時動态綁定的隐式契約關系。接口的方式是傳統的方式,編譯時确定觀察者關系,清晰明了,但通常要求有一緻的事件類型、方法簽名。而基于注解實作的機制,剛好相反,編譯時因為沒有接口的文法層面上的依賴關系,顯得不那麼清晰,至少靜态分析工具很難展示觀察者關系,但無需一緻的方法簽名、事件參數,至于多個訂閱者類之間的繼承關系,可以繼承接收事件的通知,可以看作既是其優點也是其缺點。