#EventBus(二) 上一章講解了Guava的EventBus的使用,這一章會開始模擬EventBus的源碼來實現一個簡單EventBus。首先要了解事件總線的幾個功能模塊。demo在github上。java
##1.Subscriber Subscriber模塊要作的事情就是根據接收到的事件,來執行相應的操做。這裏經過Java的反射來實現,經過調用Method的invoke方法來執行一個類中的某個方法。
###2.1 訂閱者 Subscriber.javagit
//訂閱者 public class Subscriber { //訂閱事件的類 private final Object target; //訂閱事件的方法 private final Method method; public Subscriber(Object target, Method method) { if (target == null || method == null) { throw new IllegalArgumentException("Target object and method must not be null"); } this.target = target; this.method = method; //值爲 true 則指示反射的對象在使用時應該取消 Java 語言訪問檢查。值爲 false 則指示反射的對象應該實施 Java 語言訪問檢查,能夠提升性能 this.method.setAccessible(true); } public void invoke(Object parameter) throws Exception { method.invoke(target, parameter); } }
###2.2 訂閱者的註解 Subscribe.java 這裏要模擬EventBus中的Subscribe註解,使用註解的好處是被訂閱的消息不須要特定地遵照一些約定,只要標註上這個註解,那麼就表明訂閱這個類型的消息。github
/** * 標識訂閱者 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public [@interface](https://my.oschina.net/u/996807) Subscribe { }
##2.EventBus EventBus模塊要作的就是框架
* 維護一個根據事件找到對應的訂閱者的路由ide
* 維護一個存儲消息的隊列post
###2.3 事件總線 EventBus.java性能
//事件總線 public class EventBus { //維護key是事件的class,value是這個事件全部的訂閱者的映射關係 private Map<Class<?>, List<Subscriber>> register; //存放事件的阻塞隊列 private BlockingQueue<Object> queue; public EventBus() { this.register = new HashMap<>(); queue = new LinkedBlockingDeque<>(); new Thread(new Runnable() { [@Override](https://my.oschina.net/u/1162528) public void run() { //事件的消費者 Consumer consumer = new Consumer(queue, register); consumer.start(); } }).start(); } /** * 把類的信息及其subscriber註冊到map中去 */ public void register(Object listener) { if (listener == null) { return; } Class<?> clazz = listener.getClass(); //找到當前類的全部帶有Subscribe註解的方法 for (Method method : getAnnotatedMethod(clazz)) { pushToResisterMap(listener, method); } } private void pushToResisterMap(Object listener, Method method) { if (listener == null || method == null) { return; } //獲取方法的第一個參數 Class<?> eventParamter = method.getParameterTypes()[0]; List<Subscriber> subscriberList; if (register.containsKey(eventParamter)) { subscriberList = register.get(eventParamter); subscriberList.add(new Subscriber(listener, method)); } else { subscriberList = new ArrayList<>(); subscriberList.add(new Subscriber(listener, method)); register.put(eventParamter, subscriberList); } } /** * 獲取類的全部帶有Subscribe註解的方法 */ private Set<Method> getAnnotatedMethod(Class<?> clazz) { Set<Method> annotatedMethods = new HashSet<>(); Method[] methods = clazz.getMethods(); for (Method method : methods) { Subscribe annotation = method.getAnnotation(Subscribe.class); if (annotation != null && !method.isBridge()) { annotatedMethods.add(method); } } return annotatedMethods; } /** * 向阻塞隊列裏發送事件 */ public void post(Object event) { if (event == null) { return; } try { queue.put(event); } catch (InterruptedException e) { e.printStackTrace(); } } }
##3.Consumer 消費者要作的就是不斷地從隊列中獲取新的事件,從EventBus獲取這個事件的全部訂閱者,而後讓訂閱者執行相應的方法便可 ###2.4 消費者 Consumer.java //消費者 public class Consumer {測試
private BlockingQueue<Object> queue; private Map<Class<?>, List<Subscriber>> register; public Consumer(BlockingQueue<Object> queue, Map<Class<?>, List<Subscriber>> register) { this.queue = queue; this.register = register; } public void start() { try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } while (true) { Object event; try { //從隊列裏取事件,若是沒有事件就阻塞住 event = queue.take(); } catch (InterruptedException e) { e.printStackTrace(); continue; } Class<?> clazz = event.getClass(); if (!register.containsKey(clazz)) { System.out.println("Cannot found event's subscriber"); continue; } //找到事件的subscriber,而後執行對應的事件 List<Subscriber> subscriberList = register.get(clazz); for (Subscriber subscriber : subscriberList) { try { subscriber.invoke(event); } catch (Exception e) { System.out.println("Eventbus execute event failed , " + event + "/" + subscriber); } } } } }
###2.5 訂閱事件的服務 EventService.javaui
public class EventService { public EventService(EventBus eventBus) { eventBus.register(this); } @Subscribe public void handleEvent(HelloEvent event) { if (event == null) { return; } System.out.println("handleEvent received: " + event); } @Subscribe public void doHelloEvent(HelloEvent event) { if (event == null) { return; } System.out.println("doHelloEvent received: " + event); } @Subscribe public void doGoodByEvent(GoodByEvent event) { if (event == null) { return; } System.out.println("doGoodByEvent received: " + event.toString()); } @Subscribe public void receiveGoodByEvent(GoodByEvent event) { if (event == null) { return; } System.out.println("receiveGoodByEvent received: " + event.toString()); } } class HelloEvent { private String greeting; private Date date; public void setGreeting(String greeting) { this.greeting = greeting; } public void setDate(Date date) { this.date = date; } @Override public String toString() { return "HelloEvent{" + "greeting='" + greeting + '\'' + ", date=" + date + '}'; } } class GoodByEvent { private String saying; private Date date; public void setSaying(String saying) { this.saying = saying; } public void setDate(Date date) { this.date = date; } @Override public String toString() { return "GoodByEvent{" + "saying='" + saying + '\'' + ", date=" + date + '}'; } }
###2.6 測試一下this
public class UnitTest { private EventBus eventBus; private EventService eventService; @Before public void init() { eventBus = new EventBus(); eventService = new EventService(eventBus); } private void postSeveralEvent() { HelloEvent helloEvent = new HelloEvent(); helloEvent.setGreeting("你好啊"); helloEvent.setDate(new Date()); GoodByEvent goodByEvent = new GoodByEvent(); goodByEvent.setSaying("再見啦"); goodByEvent.setDate(new Date()); eventBus.post(helloEvent); eventBus.post(goodByEvent); } @Test public void run() throws Exception { postSeveralEvent(); while (Thread.activeCount() > 2) { } } }
運行以上測試用例能夠看到,事件被成功的接收到並執行了
doHelloEvent received: HelloEvent{greeting='你好啊', date=Sun May 07 21:13:11 CST 2017}
handleEvent received: HelloEvent{greeting='你好啊', date=Sun May 07 21:13:11 CST 2017}
doGoodByEvent received: GoodByEvent{saying='再見啦', date=Sun May 07 21:13:11 CST 2017}
receiveGoodByEvent received: GoodByEvent{saying='再見啦', date=Sun May 07 21:13:11 CST 2017}
這兩篇博客是以前在記錄在印象筆記上的筆記,最近有空整理了一下~等有空了還想寫一下關於Guice和Jersey的一些博客,以前的公司用了這兩個框架。(:зゝ∠)