自用,简单记录,推荐文章JAVA | Guava EventBus 使用 发布/订阅模式 - 云+社区 - 腾讯云
依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>30.1-jre</version>
</dependency>
粗略介绍,下面这些特性都可以从源码分析中得到印证
1. 使用Subscribe注解方法来表示事件订阅,该方法必须是单参数方法,该参数即为订阅的事件类型。
2. 一个订阅者可以订阅多个事件,对应多个使用Subscribe注解的方法,
3. 不同的方法也可以订阅同一个事件。
情形1:一个订阅者中的不同方法订阅同一个事件,多个方法的执行顺序取决于方法的上下位置
情形2:多个订阅者订阅同一个事件。按注册先后顺序,决定优先执行哪个订阅者的监听方法。
两种情形都不建议,EventBus似乎不支持手动指定顺序,所以无法准确预知代码的执行顺序,理论上,一个事件触发,其处理逻辑应当清晰统一的。
4. 事件之间若存在父子关系,子事件也会被定订阅其父事件的方法接收,触发的先后顺序未验证(貌似是先子类后父类)
5. Subscriber methods cannot accept primitives 方法参数不接受原始数据类型,同时也只接受一个参数。
源码分析
一、事件总线EventBus
代码很简单,公开的方法仅有这几个,主要关注构造方法,注册,事件分发
1. 属性说明
public class EventBus {
private static final Logger logger = Logger.getLogger(EventBus.class.getName());
//id标识符
private final String identifier;
//发送事件的线程池
private final Executor executor;
//订阅者异常处理器
private final SubscriberExceptionHandler exceptionHandler;
//订阅者注册中心
private final SubscriberRegistry subscribers = new SubscriberRegistry(this);
//事件分发策略
private final Dispatcher dispatcher;
...
}
2. 构造说明
public EventBus() {
this("default");
}
public EventBus(String identifier) {
this(
identifier,
MoreExecutors.directExecutor(),
Dispatcher.perThreadDispatchQueue(),
LoggingHandler.INSTANCE);
}
public EventBus(SubscriberExceptionHandler exceptionHandler) {
this(
"default",
MoreExecutors.directExecutor(),
Dispatcher.perThreadDispatchQueue(),
exceptionHandler);
}
EventBus(
String identifier,
Executor executor,
Dispatcher dispatcher,
SubscriberExceptionHandler exceptionHandler) {
this.identifier = checkNotNull(identifier);
this.executor = checkNotNull(executor);
this.dispatcher = checkNotNull(dispatcher);
this.exceptionHandler = checkNotNull(exceptionHandler);
}
可以看到,EventBus对外暴露的构造方法,只能去修改identifier
和exceptionHandler
两个参数
对于其中涉及到的executor和dispatcher
,后面会再介绍,exceptionHandler简单提一下
/** Handles the given exception thrown by a subscriber with the given context. */
void handleSubscriberException(Throwable e, SubscriberExceptionContext context) {
checkNotNull(e);
checkNotNull(context);
try {
exceptionHandler.handleException(e, context);
} catch (Throwable e2) {
// if the handler threw an exception... well, just log it
logger.log(
Level.SEVERE,
String.format(Locale.ROOT, "Exception %s thrown while handling exception: %s", e2, e),
e2);
}
}
查看一下 handleSubscriberException 方法的调用,发现只有一处,即订阅者被分发到事件,进行事件处理时。方法的入参我们无需关心,会自动包装好。也就是说这是一个通用的事件处理过程中的异常处理回调方法,
如果我们事件处理过程中上抛了未被捕获的异常,会调用该方法进行处理。不指定exceptionHandler的话,会使用默认的异常处理器
LoggingHandler
/** Dispatches {@code event} to this subscriber using the proper executor. */
// 使用适当的executor将事件分派给该订阅者
final void dispatchEvent(final Object event) {
// 这里的executor默认为DirectExecutor,在EventBus中不支持修改
executor.execute(
new Runnable() {
@Override
public void run() {
try {
invokeSubscriberMethod(event);
} catch (InvocationTargetException e) {
bus.handleSubscriberException(e.getCause(), context(event));
}
}
});
}
3. 注册,入参为Object,即注册的事件对象可以是任意对象,实际的注册行为是由subscribers完成的,所以放在SubscriberRegistry中去介绍。
public void register(Object object) {
subscribers.register(object);
}
public void unregister(Object object) {
subscribers.unregister(object);
}
4. 事件分发,从事件注册中心subscribers中找到该事件的所有订阅者,由事件分发器dispatcher进行分发处理,核心在SubscriberRegistry和Dispatcher中,放在后边介绍
当事件发出,但是未匹配到订阅者,且自身并不是DeadEvent实例时,会发出一个DeadEvent事件,DeadEvent没有默认的监听者,我们也可以自己定义一个。
/**
* Posts an event to all registered subscribers. This method will return successfully after the
* event has been posted to all subscribers, and regardless of any exceptions thrown by
* subscribers.
*
* <p>If no subscribers have been subscribed for {@code event}'s class, and {@code event} is not
* already a {@link DeadEvent}, it will be wrapped in a DeadEvent and reposted.
*
* @param event event to 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));
}
}
二、订阅者注册中心 SubscriberRegistry
1. 内部属性
1.1 bus
一个事件总线eventBus对应一个订阅者注册中心,一个注册中心归属于一个事件总线,一一对应
/** The event bus this registry belongs to. */
@Weak private final EventBus bus;
1.2 subscribers
subscribers 维护所有订阅者与其订阅事件的关系,键为事件类型,值为订阅该事件的所有监听者
/**
* All registered subscribers, indexed by event type.
*
* <p>The {@link CopyOnWriteArraySet} values make it easy and relatively lightweight to get an
* immutable snapshot of all current subscribers to an event without any locking.
*/
private final ConcurrentMap<Class<?>, CopyOnWriteArraySet<Subscriber>> subscribers =
Maps.newConcurrentMap();
1.3 subscriberMethodsCache
subscriberMethodsCache,静态变量,所有实例共享,当一个事件对象注册到多个其他事件总线EventBus的实例对象时,这些缓存能够很大提高效率,初始化后无数据,会在进行load调用后尝试缓存加载到的数据。
/**
* A thread-safe cache that contains the mapping from each class to all methods in that class and
* all super-classes, that are annotated with {@code @Subscribe}. The cache is shared across all
* instances of this class; this greatly improves performance if multiple EventBus instances are
* created and objects of the same class are registered on all of them.
*/
private static final LoadingCache<Class<?>, ImmutableList<Method>> subscriberMethodsCache =
CacheBuilder.newBuilder()
.weakKeys()
.build(
new CacheLoader<Class<?>, ImmutableList<Method>>() {
@Override
public ImmutableList<Method> load(Class<?> concreteClass) throws Exception {
return getAnnotatedMethodsNotCached(concreteClass);
}
});
getAnnotatedMethodsNotCached方法:当缓存中未缓存该监听者信息时,会调用CacheLoader重写的load方法,从而调用此方法寻找@Subscribe注解的方法
private static ImmutableList<Method> getAnnotatedMethodsNotCached(Class<?> clazz) {
// 得到其自身及其所有父类的Class文件,也就是说会自动注册其所有父类
Set<? extends Class<?>> supertypes = TypeToken.of(clazz).getTypes().rawTypes();
Map<MethodIdentifier, Method> identifiers = Maps.newHashMap();
for (Class<?> supertype : supertypes) {
for (Method method : supertype.getDeclaredMethods()) {
if (method.isAnnotationPresent(Subscribe.class) && !method.isSynthetic()) {
// TODO(cgdecker): Should check for a generic parameter type and error out
Class<?>[] parameterTypes = method.getParameterTypes();
// 限制了@Subscribe方法参数只能有1个
checkArgument(
parameterTypes.length == 1,
"Method %s has @Subscribe annotation but has %s parameters. "
+ "Subscriber methods must have exactly 1 parameter.",
method,
parameterTypes.length);
// 限制了@Subscribe方法参数不能为原始数据类型
checkArgument(
!parameterTypes[0].isPrimitive(),
"@Subscribe method %s's parameter is %s. "
+ "Subscriber methods cannot accept primitives. "
+ "Consider changing the parameter to %s.",
method,
parameterTypes[0].getName(),
Primitives.wrap(parameterTypes[0]).getSimpleName());
MethodIdentifier ident = new MethodIdentifier(method);
if (!identifiers.containsKey(ident)) {
identifiers.put(ident, method);
}
}
}
}
return ImmutableList.copyOf(identifiers.values());
}
1.4 flattenHierarchyCache
一个全局缓存,维护了订阅者与其所有超类的对应关系,键为订阅者,值为超类的集合
/** Global cache of classes to their flattened hierarchy of supertypes. */
private static final LoadingCache<Class<?>, ImmutableSet<Class<?>>> flattenHierarchyCache =
CacheBuilder.newBuilder()
.weakKeys()
.build(
new CacheLoader<Class<?>, ImmutableSet<Class<?>>>() {
// <Class<?>> is actually needed to compile
@SuppressWarnings("RedundantTypeArguments")
@Override
public ImmutableSet<Class<?>> load(Class<?> concreteClass) {
return ImmutableSet.<Class<?>>copyOf(
TypeToken.of(concreteClass).getTypes().rawTypes());
}
});
2. 主要方法
2.1 注册
以下这几个方法是环环相扣的,关于Subscriber可以看下第三点的介绍
/** Registers all subscriber methods on the given listener object. */
void register(Object listener) {
// 找到该订阅者订阅的所有事件,以及事件对应的方法信息(封装成了Subscriber对象)
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);
}
}
/**
* Returns all subscribers for the given listener grouped by the type of event they subscribe to.
* 按事件类型划分,返回该订阅者对每个事件的订阅信息(一个事件可以对应多个Subscriber)
*/
private Multimap<Class<?>, Subscriber> findAllSubscribers(Object listener) {
Multimap<Class<?>, Subscriber> methodsInListener = HashMultimap.create();
Class<?> clazz = listener.getClass();
for (Method method : getAnnotatedMethods(clazz)) {
Class<?>[] parameterTypes = method.getParameterTypes();
Class<?> eventType = parameterTypes[0];
methodsInListener.put(eventType, Subscriber.create(bus, listener, method));
}
return methodsInListener;
}
private static ImmutableList<Method> getAnnotatedMethods(Class<?> clazz) {
try {
/*
* 从缓存中获取,如果没有,会自动加载并写入缓存中
* 该过程过于繁琐,不细究,自动加载的逻辑主要在getAnnotatedMethodsNotCached方法中
*/
return subscriberMethodsCache.getUnchecked(clazz);
} catch (UncheckedExecutionException e) {
throwIfUnchecked(e.getCause());
throw e;
}
}
/**
* 当缓存中未记录该订阅信息时,进行获取注解订阅信息的主要方法
*/
private static ImmutableList<Method> getAnnotatedMethodsNotCached(Class<?> clazz) {
Set<? extends Class<?>> supertypes = TypeToken.of(clazz).getTypes().rawTypes();
Map<MethodIdentifier, Method> identifiers = Maps.newHashMap();
for (Class<?> supertype : supertypes) {
for (Method method : supertype.getDeclaredMethods()) {
if (method.isAnnotationPresent(Subscribe.class) && !method.isSynthetic()) {
// TODO(cgdecker): Should check for a generic parameter type and error out
Class<?>[] parameterTypes = method.getParameterTypes();
checkArgument(
parameterTypes.length == 1,
"Method %s has @Subscribe annotation but has %s parameters. "
+ "Subscriber methods must have exactly 1 parameter.",
method,
parameterTypes.length);
checkArgument(
!parameterTypes[0].isPrimitive(),
"@Subscribe method %s's parameter is %s. "
+ "Subscriber methods cannot accept primitives. "
+ "Consider changing the parameter to %s.",
method,
parameterTypes[0].getName(),
Primitives.wrap(parameterTypes[0]).getSimpleName());
MethodIdentifier ident = new MethodIdentifier(method);
if (!identifiers.containsKey(ident)) {
identifiers.put(ident, method);
}
}
}
}
return ImmutableList.copyOf(identifiers.values());
}
2.2 查找订阅者
当事件总线EventBus抛出事件后,会从注册中心查找所有与此事件相关的订阅者信息
/**
* Gets an iterator representing an immutable snapshot of all subscribers to the given event at
* the time this method is called.
*/
Iterator<Subscriber> getSubscribers(Object event) {
// 从缓存中找到事件对象event的所有超类事件类型(包含自身)
ImmutableSet<Class<?>> eventTypes = flattenHierarchy(event.getClass());
List<Iterator<Subscriber>> subscriberIterators =
Lists.newArrayListWithCapacity(eventTypes.size());
for (Class<?> eventType : eventTypes) {
CopyOnWriteArraySet<Subscriber> eventSubscribers = subscribers.get(eventType);
if (eventSubscribers != null) {
// eager no-copy snapshot
subscriberIterators.add(eventSubscribers.iterator());
}
}
return Iterators.concat(subscriberIterators.iterator());
}
/**
* 方法原理同subscriberMethodsCache缓存的逻辑,不再多聊
*/
static ImmutableSet<Class<?>> flattenHierarchy(Class<?> concreteClass) {
try {
return flattenHierarchyCache.getUnchecked(concreteClass);
} catch (UncheckedExecutionException e) {
throw Throwables.propagate(e.getCause());
}
}
三、Subscriber订阅者
1. 属性
对用户实际自定义的listener对象(可以是任意类型Object)进行的封装,bus描述了该对象注册到了哪个事件总线中,target即为该对象,method描述了对应的@Subscribe方法,executor描述了用于向此订阅者分派事件的执行器。一个listener对象可以对应多个Subscriber对象(取决于@Subscribe方法的个数)
/** The event bus this subscriber belongs to. */
@Weak private EventBus bus;
/** The object with the subscriber method. */
@VisibleForTesting final Object target;
/** Subscriber method. */
private final Method method;
/** Executor to use for dispatching events to this subscriber. */
private final Executor executor;
2. 构造
构造方法私有化,同时提供了一个支持同步的静态内部类实现,重写invokeSubscriberMethod方法,通过关键字synchronized来保证在并发环境下的安全问题。仅当@Subscribe方法同时被@AllowConcurrentEvents注解修饰时,才会创建Subscriber实例
static Subscriber create(EventBus bus, Object listener, Method method) {
return isDeclaredThreadSafe(method)
? new Subscriber(bus, listener, method)
: new SynchronizedSubscriber(bus, listener, method);
}
private static boolean isDeclaredThreadSafe(Method method) {
return method.getAnnotation(AllowConcurrentEvents.class) != null;
}
private Subscriber(EventBus bus, Object target, Method method) {
this.bus = bus;
this.target = checkNotNull(target);
this.method = method;
method.setAccessible(true);
// 所有的执行器都来源于事件总线,默认为DirectExecutor
this.executor = bus.executor();
}
static final class SynchronizedSubscriber extends Subscriber {
private SynchronizedSubscriber(EventBus bus, Object target, Method method) {
super(bus, target, method);
}
@Override
void invokeSubscriberMethod(Object event) throws InvocationTargetException {
synchronized (this) {
super.invokeSubscriberMethod(event);
}
}
}
/** Dispatches {@code event} to this subscriber using the proper executor. */
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));
}
}
});
}
void invokeSubscriberMethod(Object event) throws InvocationTargetException {
try {
method.invoke(target, checkNotNull(event));
} catch (IllegalArgumentException e) {
throw new Error("Method rejected target/argument: " + event, e);
} catch (IllegalAccessException e) {
throw new Error("Method became inaccessible: " + event, e);
} catch (InvocationTargetException e) {
if (e.getCause() instanceof Error) {
throw (Error) e.getCause();
}
throw e;
}
}
dispatchEvent方法中的executor决定了事件的处理是异步还是同步的,而EventBus中的executor默认是DirectExecutor,且不允许修改,所以实际上是同步处理。如果需要异步处理,Guava EventBus 为了简化操作,提供了一个简化的方案AsyncEventBus,其构造方法中允许修改executor
public class AsyncEventBus extends EventBus {
public AsyncEventBus(String identifier, Executor executor) {
super(identifier, executor, Dispatcher.legacyAsync(), LoggingHandler.INSTANCE);
}
public AsyncEventBus(Executor executor, SubscriberExceptionHandler subscriberExceptionHandler) {
super("default", executor, Dispatcher.legacyAsync(), subscriberExceptionHandler);
}
public AsyncEventBus(Executor executor) {
super("default", executor, Dispatcher.legacyAsync(), LoggingHandler.INSTANCE);
}
}
enum DirectExecutor implements Executor {
INSTANCE;
@Override
public void execute(Runnable command) {
command.run();
}
@Override
public String toString() {
return "MoreExecutors.directExecutor()";
}
}
四、Dispatcher 事件分发器
抽象类,定义了dispatch方法模板,并提供了3种内部实现以及获取这3种实现的静态方法
abstract class Dispatcher {
static Dispatcher perThreadDispatchQueue() {
return new PerThreadQueuedDispatcher();
}
static Dispatcher legacyAsync() {
return new LegacyAsyncDispatcher();
}
static Dispatcher immediate() {
return ImmediateDispatcher.INSTANCE;
}
abstract void dispatch(Object event, Iterator<Subscriber> subscribers);
// ...
}
4.1 ImmediateDispatcher
最简单的一种,不做任何顺序限制,按照Subscribers自身遍历的顺序进行分发
/** Implementation of {@link #immediate()}. */
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);
}
}
}
4.2 PerThreadQueuedDispatcher
默认方式,保证发布在单个线程上的所有事件都按照发布的顺序调度给所有订阅者
/** Implementation of a {@link #perThreadDispatchQueue()} dispatcher. */
private static final class PerThreadQueuedDispatcher extends Dispatcher {
// This dispatcher matches the original dispatch behavior of EventBus.
/** Per-thread queue of events to dispatch. */
private final ThreadLocal<Queue<Event>> queue =
new ThreadLocal<Queue<Event>>() {
@Override
protected Queue<Event> initialValue() {
return Queues.newArrayDeque();
}
};
/** Per-thread dispatch state, used to avoid reentrant event dispatching. 防重入*/
private final ThreadLocal<Boolean> dispatching =
new ThreadLocal<Boolean>() {
@Override
protected Boolean initialValue() {
return false;
}
};
@Override
void dispatch(Object event, Iterator<Subscriber> subscribers) {
checkNotNull(event);
checkNotNull(subscribers);
Queue<Event> queueForThread = queue.get();
queueForThread.offer(new Event(event, subscribers));
if (!dispatching.get()) {
dispatching.set(true);
try {
Event nextEvent;
while ((nextEvent = queueForThread.poll()) != null) {
while (nextEvent.subscribers.hasNext()) {
nextEvent.subscribers.next().dispatchEvent(nextEvent.event);
}
}
} finally {
dispatching.remove();
queue.remove();
}
}
}
private static final class Event {
private final Object event;
private final Iterator<Subscriber> subscribers;
private Event(Object event, Iterator<Subscriber> subscribers) {
this.event = event;
this.subscribers = subscribers;
}
}
}
4.3 LegacyAsyncDispatcher
它对在单个全局队列中发布的事件进行排队。 此行为与 AsyncEventBus 的原始行为完全匹配,但在其他方面并不是特别有用,注释上说明了在保证顺序问题上的局限性。
/** Implementation of a {@link #legacyAsync()} dispatcher. */
private static final class LegacyAsyncDispatcher extends Dispatcher {
// This dispatcher matches the original dispatch behavior of AsyncEventBus.
//
// We can't really make any guarantees about the overall dispatch order for this dispatcher in
// a multithreaded environment for a couple reasons:
//
// 1. Subscribers to events posted on different threads can be interleaved with each other
// freely. (A event on one thread, B event on another could yield any of
// [a1, a2, a3, b1, b2], [a1, b2, a2, a3, b2], [a1, b2, b3, a2, a3], etc.)
// 2. It's possible for subscribers to actually be dispatched to in a different order than they
// were added to the queue. It's easily possible for one thread to take the head of the
// queue, immediately followed by another thread taking the next element in the queue. That
// second thread can then dispatch to the subscriber it took before the first thread does.
//
// All this makes me really wonder if there's any value in queueing here at all. A dispatcher
// that simply loops through the subscribers and dispatches the event to each would actually
// probably provide a stronger order guarantee, though that order would obviously be different
// in some cases.
/** Global event queue. */
private final ConcurrentLinkedQueue<EventWithSubscriber> queue =
Queues.newConcurrentLinkedQueue();
@Override
void dispatch(Object event, Iterator<Subscriber> subscribers) {
checkNotNull(event);
while (subscribers.hasNext()) {
queue.add(new EventWithSubscriber(event, subscribers.next()));
}
EventWithSubscriber e;
while ((e = queue.poll()) != null) {
e.subscriber.dispatchEvent(e.event);
}
}
private static final class EventWithSubscriber {
private final Object event;
private final Subscriber subscriber;
private EventWithSubscriber(Object event, Subscriber subscriber) {
this.event = event;
this.subscriber = subscriber;
}
}
}