搞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, Subscriber> listenerMethods = findAllSubscribers(listener);
for (Entry, Collection> entry : listenerMethods.asMap().entrySet()) {
Class> eventType = entry.getKey();
Collection eventMethodsInListener = entry.getValue();
CopyOnWriteArraySet eventSubscribers = subscribers.get(eventType);
if (eventSubscribers == null) {
CopyOnWriteArraySet 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 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 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相关技术感兴趣,可以关注我的微信公众号: