搞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 DeadEventpost(new DeadEvent(this, event));}
}

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

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

 private static final class ImmediateDispatcher extends Dispatcher {private static final ImmediateDispatcher INSTANCE = new ImmediateDispatcher();@Overridevoid 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() {@Overridepublic 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相关技术感兴趣,可以关注我的微信公众号:

详解EventBus实现原理相关推荐

  1. 【转详解步进电机工作原理】

    详解步进电机工作原理[转自知乎gk-auto] 步进电机是将电脉冲信号转变为角位移或线位移的开环控制元件.在非超载的情况下,电机的转速.停止的位置只取决于脉冲信号的频率和脉冲数,而不受负载变化的影响, ...

  2. FFmpeg入门详解--音视频原理及应用:梅会东:清华大学出版社

    大家好,我的第一本书正式出版了,可以在京东各大店铺抢购哦. <FFmpeg入门详解--音视频原理及应用:梅会东:清华大学出版社> 京东自营链接:https://item.jd.com/13 ...

  3. 初级游戏外挂编程详解 windows运行原理+游戏辅助编程 游戏外挂编程

    @TOC初级游戏外挂编程详解 windows运行原理+游戏辅助编程 游戏外挂编程 [1]什么是windows API Windows API 中文翻译过来就是windows应用程序接口(Applica ...

  4. 多维度详解redis以及原理实现,结构与应用分析

    6个方面了解redis应用及其实现原理 1.redis数据存储概述 2.string结构以及应用 3.list结构以及应用 4.hash结构以及应用 5.set结构以及应用 6.zset结构以及应用 ...

  5. 详解帧中继工作原理及作用

    详解帧中继工作原理及作用 帧中继特点 帧中继工作原理 帧中继的作用 帧中继习题 帧中继(FrameRelay)是一种用于连接计算机系统的面向分组的通信方法.它主要用在公共或专用网上的局域网互联以及广域 ...

  6. ArrayList 扩容详解,扩容原理

    ArrayList 扩容详解,扩容原理 ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长. ArrayList不是线程安全的,只能用在单线程环境下. 实现了Serializable ...

  7. 【OpenCV 4开发详解】边缘检测原理

    本文首发于"小白学视觉"微信公众号,欢迎关注公众号 本文作者为小白,版权归人民邮电出版社发行所有,禁止转载,侵权必究! 经过几个月的努力,小白终于完成了市面上第一本OpenCV 4 ...

  8. 重磅直播|中科慧眼崔峰博士详解深度相机原理及其应用

    点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 大家好,本公众号现已开启线上视频公开课,主讲人通过B站直播间,对3D视觉领域相关知识点进行讲解,并在微 ...

  9. 十年架构师详解JVM运行原理

    做Java开发的几乎都知JVM这个名词,但是由于JVM对实际的简单开发的来说关联的还是不多,一般工作个一两年(当然不包括爱学习的及专门做性能优化的什么的),很少有人能很好的去学习及理解什么是JVM,以 ...

最新文章

  1. 面试必备的C++知识(未完待续)
  2. python类型提示模块包_(任何)python模块的类型提示是什么?
  3. 在asp.net中实现回车替代Tab键
  4. 操作系统和常用软件下载
  5. java 正则 js_正则表达式在js和java中如何使用
  6. git21天打卡day21-解决合并冲突
  7. Python_Python处理JSON文件
  8. 邀请您加入移动开发专家联盟
  9. 步骤五 · 4-9 解决getElementsByClassName()兼容性 未解决
  10. 压缩图片和改变图片图形
  11. [爱奇艺]校招笔试(2017/9/11)
  12. 阿玛机器人_豪华日本声优阵容,《战斗天赋解析系统》让你耳朵怀孕!
  13. 无法打开源文件 ctype.h和.exe 进行写入
  14. 玩转Linux操作系统
  15. 2021年5月23日
  16. 边际效应以及边际效应的递减规律
  17. iOS中CAShapeLayer用法
  18. 计算机基础(10)——截图(1)——鼠标右键菜单怎么截图
  19. 从“四跨”测试看车联网产业现状和趋势
  20. Research Commentary on Recommendations with Side Information: A Survey and Research Directions

热门文章

  1. python爬取B站动态的评论总数(不含用户评论内容详情)
  2. excel表格 筛选 通过mysql语句
  3. linux搭建ssh服务器,并用xshell远程连接
  4. 上官婉儿飞天连招(玩法解析)
  5. Kotlin 开发Android app(十九):文件读写和SharedPreferences内容存储
  6. 下沉市场三巨头,趣头条全面掉队
  7. “偶遇” 爱可生 与 MYSQL 大型应用
  8. seo的日常工作内容
  9. a++ 和 ++a 的区别
  10. 【Bug解决】curl: (7) Failed connect to 192.168.159.133:8888; No route to host