优秀开源代码解析(一)Google guava之EventBus

Java极客  |  作者  /  铿然一叶
这是Java极客的第 49 篇原创文章

1、学习开源代码的收获

学习开源代码可以有以下收获:

1.学些其架构设计思想,看看怎么实现一个高可用,可扩展的架构。

2.学习一些好的java语法,毕竟你在实际代码过程中不会使用到所有的java语法,而在看源码的过程中,就有可能发现你未曾使用过,但比较巧妙的用法。

3.学习设计模式,开源代码常常会使用到一些设计模式,可以加深你对设计模式的理解。

4.学习一些基本设计原则,比如通过有界队列避免内存溢出,通过异步线程提高性能,通过依赖注入提高扩展性,可测性等等。

2、EventBus是什么

EventBus 是Google的一个开源库,它利用发布/订阅者者模式来对项目进行解耦。它可以利用很少的代码,来实现多组件间通信。

3、EventBus代码结构

EventBus代码结构如下:

类说明:

3.1、EventBus

EventBus是核心入口类,如果全都采用默认实现,只需要实现Listener,并通过调用EventBus类的方法则可完成所有功能。

3.2、SubscriberExceptionHandler

SubscriberExceptionHandler是异常处理接口,可替换自己的实现。

3.3、Executor

Executor用于异步执行Listener的监听方法,可替换自己的实现。

3.4、Dispatcher

Dispatcher是event派发接口,可替换自己的实现,默认提供了3个实现类。

3.5、SubscriberRegistry

SubscriberRegistry是事件注册类,也用于获取订阅者。

3.6、Subscriber

Subscriber是订阅者,对Listener做了封装,屏蔽了复杂的调用逻辑,使得使用者不必关心这些复杂逻辑,只要提供Listener的具体实现则可。

3.7、SynchronizedSubscriber

SynchronizedSubscriber是支持并发调用的订阅者,可以通过在Listener的事件监听方法上添加AllowConcurrentEvents注解来达到使用SynchronizedSubscriber的目的。

3.8、Subscribe

Subscribe是一个注解类,可以在任何类的方法上添加该注解来表达该方法是一个事件监听方法。

3.9、DeadEvent

DeadEvent用于记录那些已经没有订阅者的事件。

3.10、SubscriberExceptionContext

SubscriberExceptionContext是异常上下文,用于当订阅者处理异常时记录相关的上下文信息,方便异常处理实现类获得这些信息来处理异常。

4、EventBus的优秀之处

1.面向接口编程:Executor,Dispatcher,SubscriberExceptionHandler都是接口,使用者可以替换具体的实现类。

2.使用依赖注入,使得可测性增强。从图中可以看到EventBus持有Executor,Dispatcher,SubscriberExceptionHandler三个对象,它们可通过EventBus的构造器注入,这样使用者就有机会方便的替换具体的实现,或者mock一个对象来用于测试。如果它们不是可注入的,而是直接在某个方法内调用,就失去了替换的机会。当然,接口注入的方式还有很多,例如通过set方法,通过反射动态生成等等,但构造器注入是最简单,最省心的方法。

3.异步处理,提高性能。Subscriber最终处理event是通过Executor以异步线程方式执行,不会因为同步而阻塞,大大提高了性能。

4.利用Subscribe注解+反射,使得任何类的任何方法都可以成为事件监听类,而不需要实现特定的监听者接口。

5.考虑了异常处理机制,一方面提供了SubscriberExceptionHandler接口让使用者来实现具体的处理逻辑,另外一方面提供了DeadEvent类,将那些失去订阅者的事件统一归类到DeadEvent,由使用者自行实现一个Listener去处理它们。

6.通过模板方法固化了整个调用流程,使用者只需要按照要求简单实现则可使用。

5、EventBus具体使用过程

5.1、实现一个Listener

5.1.1、使用说明

Listener是一个事件监听类,一个Listener类可以通过不同的方法同时监听多个事件,任何类都可以作为Listener,但需要遵循以下要求:

1.必须在监听方法上添加Subscribe注解,用以表达该方法是一个监听方法

2.此监听方法只能有一个参数,这个参数就是要监听的事件,参数类的Class可以理解为就是要监听的EventType

5.1.2、相关代码

1.Listener例子

public class MyListener {

  // 添加Subscribe注解则表示要监听某个事件
  @Subscribe
  public void onEvent(MyEvent1 event1) {
    // do something
  }
  
  // 一个Listener可以监听多个事件
  @Subscribe
  public void onEvent(MyEvent2 event2) {
    // do something
  }
}
复制代码

2.SubscriberRegistry.java:

  // 传入的clazz类就是Listener的Class
  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()) {
        // 这里查找方法上是否有Subscribe注解
        if (method.isAnnotationPresent(Subscribe.class) && !method.isSynthetic()) {
          // TODO(cgdecker): Should check for a generic parameter type and error out
          Class<?>[] parameterTypes = method.getParameterTypes();
          // 这里检查方法的参数只有1个
          checkArgument(
              parameterTypes.length == 1,
              "Method %s has @Subscribe annotation but has %s parameters."
                  + "Subscriber methods must have exactly 1 parameter.",
              method,
              parameterTypes.length);

          MethodIdentifier ident = new MethodIdentifier(method);
          if (!identifiers.containsKey(ident)) {
            identifiers.put(ident, method);
          }
        }
      }
    }
    return ImmutableList.copyOf(identifiers.values());
  }
复制代码

5.2、构造EventBus

5.2.1、使用说明

1.在一个系统中,根据用途不同,可以同时存在多个EventBus,不同的EventBus通过identifier来识别。

2.为方便使用,EventBus提供了多个构造器,使用者可以根据需要注入不同的实现类,最简单的构造器是一个无参构造器,全部使用默认实现。

3.在实际使用过程中,可以使用一个单例类来持有EventBus实例,如有需要,可以持有不同的EventBus实例用于不同的用途。

5.2.2、相关代码

1.EventBus.java

  //无参构造器
  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);
  }
  
  //注入所有参数构造器,需注意的是,此方法不是public的,只能在包内访问。
  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);
  }
复制代码

5.3、注册Listener

5.3.1、使用说明

以上两步完成后,即可通过EventBus来注册Listener。

5.3.2、相关代码

1.EventBus.java

  private final SubscriberRegistry subscribers = new SubscriberRegistry(this);
  
  public void register(Object object) {
    subscribers.register(object);
  }
复制代码

2.SubscriberRegistry.java

  void register(Object listener) {
    // 查找有Subscribe注解的方法,并封装为Subscriber,Multimap的key记录的Class就是要监听的对象的Class
    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);
    }
  }
复制代码

5.4、发布Event

5.4.1、使用说明

发布事件比较简单,只要在需要发布事件的地方得到EventBus实例,然后调用post方法则可。

5.4.2、相关代码

1.EventBus.java

  public void post(Object event) {
    // 根据event找到订阅者,这里实际是根据event.Class来查找,也即和Listener的监听方法的参数的Class一致。
    Iterator<Subscriber> eventSubscribers = subscribers.getSubscribers(event);
    if (eventSubscribers.hasNext()) {
      //通过dispatcher来派发事件,最终调用的是Subscriber的dispatchEvent方法
      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));
    }
  }
复制代码

2.Subscriber.java

  final void dispatchEvent(final Object event) {
    // 通过executor来实现异步调用,这个executor在EventBus是可注入的,可以注入修改后的实现类
    executor.execute(
        new Runnable() {
          @Override
          public void run() {
            try {
              invokeSubscriberMethod(event);
            } catch (InvocationTargetException e) {
              // 这里最终调用的是在EventBus中注入的SubscriberExceptionHandler,可以注入修改后的实现类
              bus.handleSubscriberException(e.getCause(), context(event));
            }
          }
        });
  }
  
  void invokeSubscriberMethod(Object event) throws InvocationTargetException {
    try {
      //这里的method就是Listener的监听方法,target就是Listener对象,event就是这个监听方法的入参
      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;
    }
  }
复制代码

6、Google guava源码地址

github.com/google/guav…

end.


Java极客站点: javageektour.com/

猜你喜欢

转载自juejin.im/post/5e770e98e51d452701798a77