Guava EventBus源码分析(二):注册订阅者方法
Guava EventBus是一个发布-订阅模式的组件通信,但组件不需要显式地注册到其它组件中。
本文关注EventBus注册订阅者方法源码分析。
一、概述:
首先回顾一下第一讲提到的EventBus的三个核心方法。
public class EventBus {
//注册订阅者方法
public void register(Object object)
public void unregister(Object object)
public void post(Object event)
}
- register:注册一个订阅者,本文的关注点。
- unregister:注销一个订阅者
- post:广播一个事件
二、源码分析:
EventBus的所有订阅者由一个SetMultimap来管理,key为事件类(即以时间类类型为消息主题),value为订阅了这个事件的多个订阅者EventSubscriber(封装了订阅者方法和它所在的对象)。
这个SetMultimap并不是线程安全的,EventBus提供了一个可重入的锁来保证线程安全。
/**
* All registered event subscribers, indexed by event type.
*
* <p>This SetMultimap is NOT safe for concurrent use; all access should be
* made after acquiring a read or write lock via {@link #subscribersByTypeLock}.
*/
private final SetMultimap<Class<?>, EventSubscriber> subscribersByType =
HashMultimap.create();
private final ReadWriteLock subscribersByTypeLock = new ReentrantReadWriteLock();
下面是具体的注册订阅者的实现:
首先肯定从入口方法讲解:EventBus.register
public void register(Object object) {
Multimap<Class<?>, EventSubscriber> methodsInListener =
finder.findAllSubscribers(object);
subscribersByTypeLock.writeLock().lock();
try {
subscribersByType.putAll(methodsInListener);
} finally {
subscribersByTypeLock.writeLock().unlock();
}
}
可以看到register方法中主要完成了两件事
1. 找到所有带@Subscribe标签的订阅者。
2. 把他们注册到EventBus中去。
先来看看如何找到带有@Subscribe标签的订阅者的:
EventBus解析订阅的策略基于接口:
interface SubscriberFindingStrategy {
//扫描该对象的所有带有@Subscribe标签的方法,包括超类和接口的方法。
Multimap<Class<?>, EventSubscriber> findAllSubscribers(Object source);
}
EventBus有一个默认的实现:AnnotatedSubscriberFinder
我们来看看这个AnnotatedSubscriberFinder.findAllSubscribers的实现
/**
This implementation finds all methods marked with a {@link Subscribe} annotation.
*/
@Override
public Multimap<Class<?>, EventSubscriber> findAllSubscribers(Object listener) {
Multimap<Class<?>, EventSubscriber> methodsInListener = HashMultimap.create();
Class<?> clazz = listener.getClass();
for (Method method : getAnnotatedMethods(clazz)) {
Class<?>[] parameterTypes = method.getParameterTypes();
Class<?> eventType = parameterTypes[0];
EventSubscriber subscriber = makeSubscriber(listener, method);
methodsInListener.put(eventType, subscriber);
}
return methodsInListener;
}
在这个方法中:
1. 通过反射找到所有的带有@Subscribe注解的方法。
2. 将方法的第一个参数(即事件取出来作为key),方法本身作为订阅者subscriber,注册进Multimap
private static ImmutableList<Method> getAnnotatedMethods(Class<?> clazz) {
try {
return subscriberMethodsCache.getUnchecked(clazz);
} catch (UncheckedExecutionException e) {
throw Throwables.propagate(e.getCause());
}
}
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 getAnnotatedMethodsInternal(concreteClass);
}
});
可以看到是到缓存中去取这个类的订阅者方法,那么具体的实现就在缓存的加载方法中,我们来看AnnotatedSubscriberFinder#getAnnotatedMethodsInternal
private static ImmutableList<Method> getAnnotatedMethodsInternal(Class<?> clazz) {
//getTypes()取出该类所有extend和implement的类,rawTypes()得到运行时的这些类,简单来说去掉泛型。如List<String> --> List
Set<? extends Class<?>> supers = TypeToken.of(clazz).getTypes().rawTypes();
Map<MethodIdentifier, Method> identifiers = Maps.newHashMap();
for (Class<?> superClazz : supers) {
for (Method superClazzMethod : superClazz.getMethods()) {
//如果方法带有@Subscribe注解并且不是一个桥方法(bridge method)
if (superClazzMethod.isAnnotationPresent(Subscribe.class)
&& !superClazzMethod.isBridge()) {
Class<?>[] parameterTypes = superClazzMethod.getParameterTypes();
if (parameterTypes.length != 1) {
throw new IllegalArgumentException("Method " + superClazzMethod
+ " has @Subscribe annotation, but requires " + parameterTypes.length
+ " arguments. Event subscriber methods must require a single argument.");
}
MethodIdentifier ident = new MethodIdentifier(superClazzMethod);
if (!identifiers.containsKey(ident)) {
identifiers.put(ident, superClazzMethod);
}
}
}
}
//返回所有订阅者方法
return ImmutableList.copyOf(identifiers.values());
}
在这个方法中,取出该类和所有extend和implement的类的所有带有@Subscribe注解的方法,并且这个方法不是一个桥方法(简单讲:为了实现泛型方法的重写在底层自动生成的对开发者不可见的方法),且只有一个参数,这个参数就是订阅者订阅的事件类。
附录一:
EventSubscriber表示一个具体的订阅者,属性为订阅者方法和它所在的对象。
class EventSubscriber {
/** Object sporting the subscriber method. */
private final Object target;
/** Subscriber method. */
private final Method method;