详解EventBus实现原理

搞Java开发,阅读源码是家常便饭。唯有如此,才能不断吸收先进的设计思想,提高个人技术水平。下面以EventBus核心实现为引子来分析google大神是怎么优化观察者模式的。

 

前文已有EventBus用法相关示例,此处不再多言。EventBus核心类主要有四个:

EventBus
SubscriberRegistry
Dispatcher
Subscriber

没有看错,以上4个类完成了EventBus的主要功能。下面依次分析这些类的关键实现方法。

 

EventBus类属于资源管理类,大部分资源和操作都在该类实现。其属性如下所示:

private final String identifier;
private final Executor executor;
private final SubscriberExceptionHandler exceptionHandler;
private final SubscriberRegistry subscribers = new SubscriberRegistry(this);
private final Dispatcher dispatcher;

其中,identifier无需赘述,简单认为是EventBus实例的id即可,主要用于区分不同EventBus实例;executor是订阅方法的实际执行者;exceptionHandler用于处理异常;subscribers缓存了事件和对应消费者列表关系;dispatcher用于分发事件。

 

EventBus类的register方法,如下所示:

public void register(Object object) {
   subscribers.register(object);
}

说明消费者(本文消费者和订阅者表示相同的意思)注册操作委托给了SubscriberRegistry类,继续深入:

void register(Object listener) {
   Multimap<Class<?>, Subscriber> listenerMethods = findAllSubscribers(listener);

   for (Entry<Class<?>, Collection<Subscriber>> entry : listenerMethods.asMap().entrySet()) {
     Class<?> eventType = entry.getKey();
     Collection<Subscriber> eventMethodsInListener = entry.getValue();

     CopyOnWriteArraySet<Subscriber> eventSubscribers = subscribers.get(eventType);

     if (eventSubscribers == null) {
       CopyOnWriteArraySet<Subscriber> newSet = new CopyOnWriteArraySet<>();
       eventSubscribers =
           MoreObjects.firstNonNull(subscribers.putIfAbsent(eventType, newSet), newSet);
    }

     eventSubscribers.addAll(eventMethodsInListener);
  }
}

SubscriberRegistry类register方法的作用是分类缓存事件和消费者关系。用图描述如下:

图片

register方法根据Event类型把查询到的所有subscriber(订阅者)进行分类。其中,同类事件的subscriber被组织在一个链表结构中,然后缓存在ConcurrentMap字典中。这也是EventBus能够支持同时处理多种不同事件类型的直接原因。注意,subscribers如果存在Event类型的key值,那么其对应的value值至少有一个,因为由Subscribe注解的方法必然有一个Event类型的参数。

 

订阅者被成功注册之后,这时EventBus的post方法就可以发布消息了。

public void post(Object event) {
   Iterator<Subscriber> eventSubscribers = subscribers.getSubscribers(event);
   if (eventSubscribers.hasNext()) {
     dispatcher.dispatch(event, eventSubscribers);
  } else if (!(event instanceof DeadEvent)) {
     // the event had no subscribers and was not itself a DeadEvent
     post(new DeadEvent(this, event));
  }
}

首先根据Event类型获取到Subscriber(订阅者)迭代器,然后通过Dispatcher对象路由事件。如果eventSubscribers为空,说明该事件还没有注册订阅者,则根据事件类型,封装事件,并发布。

 

以Dispatcher的一个静态内部类ImmediateDispatcher为例说明路由分发:

 private static final class ImmediateDispatcher extends Dispatcher {
   private static final ImmediateDispatcher INSTANCE = new ImmediateDispatcher();

   @Override
   void dispatch(Object event, Iterator<Subscriber> subscribers) {
     checkNotNull(event);
     while (subscribers.hasNext()) {
       subscribers.next().dispatchEvent(event);
    }
  }
}

循环调用Subscriber对象的dispatchEvent方法,即遍历订阅者链表,并调用链表元素对象中的dispatchEvent方法来处理事件(这与传统的观察者模式触发事件更新方式一模一样)。

 

Subscriber分发事件方法:

final void dispatchEvent(final Object event) {
   executor.execute(
       new Runnable() {
         @Override
         public void run() {
           try {
             invokeSubscriberMethod(event);
          } catch (InvocationTargetException e) {
             bus.handleSubscriberException(e.getCause(), context(event));
          }
        }
      });
}

使用executor调用订阅者对象中被Subscribe注解的方法,即触发事件更新操作。invokeSubscriberMethod方法是Java中典型的通过反射调用对象方法的实例,此处不再赘言。

 

EventBus支持同步和异步方式执行订阅方法,默认是同步方法,因为executor是一个同步执行器。如果想使用异步方式,那么executor只需要重新用线程池赋值即可。

 

最后,如果在调用订阅方法时出现异常,则会触发exceptionHandler的handleException方法。

小结

综上所述,EventBus实现原理还是比较简单的。作者为了方便用户使用,使用Subscribe注解而不是接口来定义消费者;为了支持不同事件分发,根据事件类型对消费者进行分类。可以说,EventBus能够满足大部分场景下的观察者模式的实现。

 

写在最后,在阅读EventBus源码过程中,我们发现它选择使用CopyOnWriteArraySet缓存订阅者,那么我在这里提出一个问题COW(Copy-On-Write)和RCU(Read-Copy-Update,Linux内核锁之一,适用于读多写少场景)有什么区别?如果想知道答案,可以关注我接下来的相关文章。

如果对Java相关技术感兴趣,可以关注我的微信公众号:

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值