Google-Guava-EventBus source Interpretation

Guava is a Google open source Java foundation class library, which is widely used within Google. Guava offers many functional modules such as: collections, concurrency library, caching, EventBus is one module, Benpian combination EventBus source to talk about its design and implementation.

Overview

First, let's preview the entire class diagram EventBus module:

Class and not more than almost too much inheritance.

Below, we look at the various categories of responsibilities:

EventBus: core classes, representing an event bus. Publish events initiated by it.
AsyncEventBus: at the time of distribution of events, pressed into a global distribution asynchronous mode queue.
Subscriber: an event of processor abstraction, encapsulation of the event subscribers and a processor, and is responsible for event processing (semantic class class name and a little unclear, the follow-up talks to).
SubscriberRegistry: Subscribe to the registry, which is used to store the corresponding relationship with the Subscriber Event in order to publish a EventBus in the event, you can find its corresponding Subscriber.
Dispatcher: event dispatcher, which defines the distribution policy events.
@Subscribe: for the annotation processor identifies the event, when EventBus publish an event, the corresponding Subscriber will be notified and execute the event handler.
@AllowConcurrentEvents: The annotations used in conjunction with @Subscribe, processing method identifies the subscriber is thread-safe, the annotation is also used to identify the method may be EventBus executed in a multithreaded environment.
DeadEvent: Dead Letter (those who did not subscribe to events of interest) object.
SubscribeExceptionHandler: Subscribers thrown processors.
SubscribeExceptionContext: Subscribers context object thrown exception.
Before each class are broken down, we'll look at the relationship between the various categories:

Minute "class" interpretation

EventBus

There are several fields:
identifier The: Event Bus logo, which is described in an application can have more of EventBus. If you do not specify its value, it will be "default" as its default name.
executor: it is an instance of Executor interface for performing a method of handling events subscribers. It should be noted that the instantiation of this field is EventBus internal constructor is not injected from the outside coming in, another opportunity to perform real subscriber method is not the responsibility of the EventBus, but by the Subscriber, so the field It will be publicly accessible to the outside.
exceptionHandler: it is an instance of SubscribeExceptionHandler for processing subscriber thrown when the event handler for exception. EventBus may receive an externally defined exception handler, the internal default log record processor may also be employed.
subscribers: Subscribers registry for all events and event handlers storage, subscription object correspondence.
dispatcher: Event dispenser, for distributing the event to event handler subscription object, the object in the constructor EventBus internal initialization, the default implementation is PerThreadQueuedDispatcher, the dispenser into the event queue, and to ensure that the transmission on the same thread the event can be distributed to all subscribers in the order of their release.
EventBus provides several core methods:
the Register: Sign up for Subscriber;
the unregister: unregister had up for Subscriber;
POST: publishing events;
you can EventBus seen as a proxy, the true realization of these methods who are above these objects.

AsyncEventBus

EventBus a support asynchronous release mode, it overrides the default constructor EventBus, specifying an asynchronous dispatcher: LegacyAsyncDispatcher, this distribution is based on a global queue staging events unpublished.
Subscriber

Subscriber name mentioned before also is relatively easy to confuse. This class seems to indicate the name of a subscriber object, but in fact is used to package "a subscriber of an event handler" object. Because when a plurality of present methods is marked when the processing @Subscribe, then each of the processing methods corresponding to a separate instance of the object Subscriber subscribers. I personally feel that the name of their specific semantic some confusion. Of course, perhaps the realization believe: an object and an event handler is a Subscriber, then it is no problem. For convenience therefore understood that you can think of it as a target package and a subscriber a subscriber processor entity class method.
Subscriber access level is package, it also assumed responsibility for the implementation of event processing. It is created by a create static factory method:
static for Subscriber create (EventBus Bus, Object listener, Method, Method) {
    return isDeclaredThreadSafe (Method)
        new new for Subscriber (Bus, listener, Method)?
        : New new SynchronizedSubscriber (Bus, listener, Method);
  }

It takes three parameters:
Bus: EventBus example, the actuator acquires an event (Executor) through which the
listener: real subscriber objects
method: Method instance event subscription processing method of an object
in an implementation, it first determines the whether the processor is marked on methods @AllowConcurrentEvents notes, if so, instantiate an instance of the Subscriber class; if not, the method does not allow eventbus call processor in a multi-threaded environment, so here specifically for this purpose provides a subscriber object synchronization: SynchronizedSubscriber to ensure thread safety.
One of two methods of the class key:
the dispatchEvent:
Final void the dispatchEvent (Final Event Object) {
    Executor.execute (the Runnable new new () {
      @Override
      public void RUN () {
        the try {
          invokeSubscriberMethod (Event);
        } the catch (E a InvocationTargetException ) {
          bus.handleSubscriberException (e.getCause (), context (Event));
        }
      }
    });
  }

It calls a multi-threaded execution unit to execute the event handler method.
Another way: invokeSubscriberMethod reflectively invoke event handler method.
Further, the class equals method of Object override and were identified as final. Mainly to avoid the same object for a repeat event subscription, there will be other operations in the corresponding sentence in SubscriberRegistry. Of course there Subscriber also override the hashCode method and final. This is the best practice, do not talk, do not know if you can go and see "Effective Java".
The class also has internal class that we talked about above SynchronizedSubscriber, it inherits the Subscriber, Subscriber synchronize with the only difference is in the execution invokeSubscriberMethod done.
SubscriberRegistry

Subscribe for maintaining the relationship with the events of a single EventBus. Objects internally to store subscriber relationship is complicated and java Map under contract: ConcurrentMap, Class object to the map is a bond, the value of type is CopyOnWriteArraySet <Subscriber> collection types.
SubscriberRegistry EventBus directly dependent objects, in the constructor of the need to inject EventBus instance.
SubscriberRegistry There are two key examples of methods: register / unregister.
register

Receiving subscriber object as a parameter and establish a relationship with the Subscriber's Event.
We take a look at its implementation:
void the Register (Object listener) {
    Multimap <Class <>, up for Subscriber?> ListenerMethods = findAllSubscribers (listener);
 
    for (Map.Entry <Class <>, Collection <up for Subscriber >> entry:? ListenerMethods . .asMap () the entrySet ()) {
      <?> Class entry.getKey the eventType = ();
      Collection <for Subscriber> eventMethodsInListener entry.getValue = ();
 
      CopyOnWriteArraySet <for Subscriber> eventSubscribers = subscribers.get (the eventType);
 
      IF (eventSubscribers null ==) {
        CopyOnWriteArraySet <for Subscriber> = new new CopyOnWriteArraySet the newset <for Subscriber> ();
        eventSubscribers MoreObjects.firstNonNull = (
            subscribers.putIfAbsent(eventType, newSet), newSet);
      }
 
      eventSubscribers.addAll(eventMethodsInListener);
    }
  }

It first gets a Multimap instance (which is a multi-value type Map Google Guava Collections Framework provides, that a key can correspond to multiple value), the Multimap about the event for all subscribers within the store event type the processor set of methods, which is key for the Class type of event. Get here asMap for which the map view by cycle, to a plurality of values stored in a corresponding Multimap Collection.
That is for each entry loop herein, it is a group represented by a set of event Subscriber Class corresponding instance, i.e. eventMethodsInListener.
The Class object is then acquired from the registry in the event corresponding to the set storage Subscriber instance, if the set does not exist it is created, then the event handler method all subscribers are added to the registry.
unregister

unregister implementation is somewhat similar with the register, first find the correspondence between the subscribers and all types of events processors. Then, through all types of events, remove all instances Subscriber for the current subscribers.
findAllSubscribers

register / unregister method are called findAllSubscribers method, it has some special, you should carry out a separate mention.
findAllSubscribers for finding correspondence between the types of events and event handlers. Find notes the need to involve reflection, marked on the method to obtain comments by reflection. Because Guava taken against registered EventBus is "implicit contract" instead of interfaces such "explicit contract." The existence of classes and interfaces inheritance, and all of a subscriber is likely its parent (or parent class implements an interface) also subscribed to an event. So here needs to find follow up the inheritance chain to find whether or not the parent class is also marked annotated, code implementation:
  Private Multimap <? Class <>, for Subscriber> findAllSubscribers (Object listener) {
    Multimap <? Class <>, for Subscriber> methodsInListener HashMultimap.create = ();
    Class listener.getClass clazz = (); <?>
    for (Method, Method: getAnnotatedMethods (clazz)) {
      <?> Class [] The parameterTypes Method.getParameterTypes = ();
      Class the eventType = <?> The parameterTypes [0];
      methodsInListener.put (the eventType, Subscriber.create (Bus, listener, Method));

    return methodsInListener;
  }

Also involved in this issue, as well as acquiring Subscriber instance method according to the type of event: getSubscribers.
getSubscribers

  Iterator<Subscriber> getSubscribers(Object 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());
  }

Dispatcher

dispatcher for distributing events to the Subscriber. It different internal implementation in different scenarios of sequential events for providing a plurality of distribution. Dispatcher is an abstract class that defines a central abstract method:
abstract void dispatch (Object Event, the Iterator <for Subscriber> subscribers);

The method used for a specified event distributed to all subscribers.
Also provides three different distribution is achieved in Dispatcher:

PerThreadQueuedDispatcher

It is commonly used to build a queue for each thread for staging the event object. Ensure that all events are sent from a single thread in the order they publish in. Guarantee issued from a single thread, nothing special, mainly within the definition of a queue, put it in a ThreadLocal to associate with a particular thread.

LegacyAsyncDispatcher

Achieve another asynchronous dispenser: LegacyAsyncDispatcher, before the introduction AsyncEventBus when mentioned, it is this implementation to dispatch events.
It internally by a ConcurrentLinkedQueue <EventWithSubscriber> global queues to store events. From the key methods: Implement dispatch of view, it is the main difference with PerThreadQueuedDispatcher difference in two cycles (here based on the way the event queue buffer, certainly there will be two cycles: the queue event loop cycle to take and send Subscriber).
PerThreadQueuedDispatcher: two layers of nested loops to traverse the outer layer is taken event queue memory is traversed event subscription processor.
LegacyAsyncDispatcher: it is one after two cycles. We are walking the event in front of a subscription processor, and build a solid object into the event queue. After traversing the loop is a event queue of entities, entity object removal events distributed events.
ImmediateDispatcher

In fact, more than two distribution achieved based on the intermediate queue can be seen as an asynchronous mode and synchronous mode ImmediateDispatcher is: as long as the event will be distributed immediately and processed immediately. From the point of view similar to sensory ImmediateDispatcher linear and sequential execution, and cohort multithreaded manner converge to a common queue by the diverging polymerized model. Therefore, ImmediateDispatcher distribution approach is a depth-first way, using queue is a breadth-first manner.
DeadEvent

It is a physical object that encapsulates the event no subscribers. DeadEvent by two attributes:
Source: Event source (usually refers to publishing events EventBus objects)
Event: The event object
produced DeadEvent objects: When an event is issued by an instance of a EventBus, and did not find the event itself and subscribers examples are not a DeadEvent time, will build EventBus instance DeadEvent class.
to sum up

Guava's EventBus source is quite simple and clear. From the source point of view, it is something common Observer design, to renounce the use of a unified interface, unified event object type. In favor of binding annotation-based scanning mode.
In fact, whether it is mandatory to achieve a unified interface, or annotation-based implementations are in to construct a relationship (or meet certain contract). Obviously the way the interface is mandatory on the compiler level of explicit contracts, and notes the way it is implicit contractual relationship dynamic binding at runtime. Interface in a traditional way, compile-time to determine the relationship between the observer, clarity, but usually require the same type of event, the method signature. The annotation-based implementation mechanisms, just the opposite, because the compile-time dependencies on the grammatical level there is no interface, it is not so clear, at least static analysis tool is difficult to show the viewer relationship, but not necessarily the same method signature event parameters, as inheritance relationship between multiple subscribers classes can inherit to receive notification of events, which can be seen as both an advantage and disadvantage.

Published 29 original articles · won praise 14 · views 10000 +

Guess you like

Origin blog.csdn.net/weixin_43778179/article/details/100156205