Guava EventBus源码分析(二):注册订阅者方法

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;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值