#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的一些博客,以前的公司用了这两个框架。(:зゝ∠)