Excellent open-source code analysis (a) Google guava of EventBus

Java Geeks | Author /   Kengran leaf
This is a Java geeks of 49 original articles

1, open source learning gains

Learning open source can have the following benefits:

1. learn some of its architectural design, take a look at how to implement a highly available, scalable architecture.

2. Learn some good java syntax, after all, you will not be using all of the java syntax in actual code process, and in the process look at the source code, there are likely to find that you have not used before, but more clever usage.

3. Learn design patterns, open source often use some design patterns can deepen your understanding of design patterns.

4. learn some basic design principles, such as by bounded queue to avoid memory overflow, improve performance by the asynchronous thread, by improving scalability dependency injection, testability and the like.

2. What is EventBus

EventBus Google is an open source library that uses a publish / subscribe model to those who decouple the project. It can use very little code to implement communication between multiple components.

3, EventBus code structure

EventBus code structure is as follows:

Class Description:

3.1、EventBus

EventBus entrance is the core class, if all the default implementation, only need to implement Listener, and all functions can be completed by EventBus class method call.

3.2、SubscriberExceptionHandler

SubscriberExceptionHandler abnormal processing interface, replaceable own implementation.

3.3、Executor

Executor Listener listens for asynchronous execution method, alternative own implementation.

3.4、Dispatcher

Dispatcher is the event dispatch interface that can replace your own implementation, provides three default implementation class.

3.5、SubscriberRegistry

SubscriberRegistry class event registration is also used to get subscribers.

3.6、Subscriber

Subscriber is a subscriber, to do Listener package, shielding the complexity of call logic, so that the user does not have to care about these complex logic, as long as you can provide specific Listener implementation.

3.7、SynchronizedSubscriber

SynchronizedSubscriber is subscriber support concurrent calls, by adding AllowConcurrentEvents comment on the event listener method Listener to achieve the purpose of use SynchronizedSubscriber.

3.8、Subscribe

Subscribe is an annotation class, you can add annotations to the expression of this method is an event listener method on any class method.

3.9、DeadEvent

DeadEvent used to record events that have no subscribers.

3.10、SubscriberExceptionContext

SubscriberExceptionContext abnormal context for context information related to the recording when the subscriber exception handling, exception handling easy implementation class to obtain the information to handle the exception.

4, EventBus outstanding between

1. Oriented Programming Interface: Executor, Dispatcher, SubscriberExceptionHandler are interfaces, a user can replace a specific category.

2. Use dependency injection, so that testability enhancement. Can be seen from FIG EventBus hold Executor, Dispatcher, SubscriberExceptionHandler three objects, which can be injected through EventBus constructor, so users have the opportunity to easily replace the specific implementation, or a mock object for testing. If they are not injectable, but within a certain method calls directly, you lose the opportunity to replace. Of course, there are many ways interface injection, for example by the method set, dynamically generated by reflection, etc., but the configuration is the easiest, most worry the injection method.

3. asynchronous processing and improve performance. Subscriber final event processing is executed in an asynchronous thread way through Executor, it will not be synchronized blocked, greatly improving performance.

4. Use + Subscribe annotations reflection, such that any class of any method can be an event listener class without having to implement specific listener interface.

5. Consider the exception handling mechanism, on the one hand provides SubscriberExceptionHandler interface to allow users to achieve specific processing logic, on the other hand provides DeadEvent categories, those events would lose subscribers to DeadEvent unified classification, implemented by the users themselves a Listener to deal with them.

6. curing the entire call flow through the template method, users need only a simple implementation can be used as required.

5, EventBus specific use

5.1, to achieve a Listener

5.1.1 Instructions

Listener is an event listener class, a class Listener can listen to multiple events simultaneously in different ways, any class can be used as Listener, but need to follow the following requirements:

1. Subscribe must add annotations on listener method is a method for the expression of the listener method

2. This method can only monitor a parameter, this parameter is the event to listen, parameters can be understood as class Class is to listen EventType

5.1.2, the relevant code

1.Listener examples

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, construction EventBus

5.2.1 Instructions

1. In a system, according to different purposes, there may be multiple simultaneous EventBus, different EventBus identified by identifier.

2. To facilitate use, a plurality of constructors EventBus provided, the user can inject different implementation class required, the most simple structure is a constructor with no arguments, all use the default implementation.

3. In actual use, the class may be used to hold a single embodiment EventBus instance, if necessary, can hold different EventBus examples of different purposes.

5.2.2, the relevant code

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, registered Listener

5.3.1 Instructions

After the above two steps, you can register Listener by EventBus.

5.3.2, the relevant code

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, ​​released Event

5.4.1 Instructions

Publishing events is relatively simple, as long as the need to publish local events to get EventBus instance, you can then call the post method.

5.4.2, the relevant code

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 source address

github.com/google/guav…

end.


Java Geek site: javageektour.com/

Guess you like

Origin juejin.im/post/5e770e98e51d452701798a77