Java geek

1, learn the harvest of open source code

You can learn the following from studying open source code:

1. Learn some of its architectural design ideas and see how to achieve a highly available, extensible architecture.

2. Learn some good Java syntax. After all, you won’t use all the Java syntax in actual code, and while looking at the source code, you’ll probably find clever uses that you haven’t used before.

3. Learn design patterns. Open source code often uses some design patterns to deepen your understanding of them.

4. Learn some basic design principles, such as avoiding memory overflow through bounded queues, improving performance through asynchronous threads, improving scalability and testability through dependency injection, etc.

2. What is EventBus

EventBus is an open source library from Google that leverages the publish/subscriber pattern to decouple projects. It can use very little code to achieve communication between multiple components.

3. EventBus code structure

The EventBus code structure is as follows:

Class description:

3.1, EventBus

EventBus is the core entry class. If you use the default implementation, you only need to implement the Listener and call the methods of the EventBus class to complete all functions.

3.2, SubscriberExceptionHandler

SubscriberExceptionHandler is exception handling interface, can replace your own implementation.

3.3, Executor

Executor uses a listening method to execute listeners asynchronously, in lieu of its own implementation.

3.4, the Dispatcher

The Dispatcher is the Event dispatch interface, which can replace its own implementation. It provides three implementation classes by default.

3.5, SubscriberRegistry

The SubscriberRegistry class is an event registration class that is also used to get subscribers.

3.6, the Subscriber

Subscriber is the Subscriber, which encapsulates the Listener and shields the complex call logic, so that the user does not need to care about the complex logic, as long as the specific implementation of the Listener is provided.

3.7, SynchronizedSubscriber

SynchronizedSubscriber is a subscriber that supports concurrent calls and can be used by adding an AllowConcurrentEvents annotation to the Listener’s event Listener method.

3.8, the Subscribe

Subscribe is an annotation class that can be added to a method of any class to indicate that the method is an event-listening method.

3.9, DeadEvent

DeadEvent is used to log events that no longer have subscribers.

3.10, SubscriberExceptionContext

SubscriberExceptionContext is abnormal context for when the subscriber with abnormal records related to the context information, convenient exception handling implementation class to get the information to handle exceptions.

4. What’s great about EventBus

1. Programming to an interface: Executor, the Dispatcher, SubscriberExceptionHandler interface, users can replace the concrete implementation class.

2. Use dependency injection to enhance testability. Can be seen from the figure EventBus hold Executor, the Dispatcher, SubscriberExceptionHandler three objects, they can through the EventBus constructor injection, so users will have the opportunity to easily replace the specific implementation, Or mock an object for testing. If they are not injectable and are called directly within a method, the opportunity for replacement is lost. Of course, there are many other ways to inject interfaces, such as through set methods, dynamic generation through reflection, and so on, but constructor injection is the simplest and most effortless way.

3. Asynchronous processing improves performance. Subscriber final processing events are executed by Executor in an asynchronous thread and do not block for synchronization, greatly improving performance.

4. Using Subscribe annotation + reflection, any method of any class can become an event listener class without implementing a specific listener interface.

5. Considering the exception handling mechanism, on the one hand provides SubscriberExceptionHandler interface allows users to achieve specific processing logic, on the other hand provides DeadEvent class, to those who lost subscribers events classified into DeadEvent unity, It is up to the user to implement a Listener to process them.

6. Through the template method to solidify the whole call process, users only need to follow the requirements of simple implementation can be used.

5. Specific use process of EventBus

5.1. Implement a Listener

5.1.1 Instructions for Use

A Listener class can listen for multiple events using different methods at the same time. Any class can be used as a Listener, but it must comply with the following requirements:

1. A Subscribe comment must be added to a listening method to indicate that the method is a listening method

2. The listener method can only take one parameter, which is the event to listen for. The Class of the parameter can be interpreted as the EventType to listen for

5.1.2 Relevant codes

1. The Listener

public class MyListener {

  // Subscribe to listen to an event
  @Subscribe
  public void onEvent(MyEvent1 event1) {
    // do something
  }
  
  // A Listener can listen for multiple events
  @Subscribe
  public void onEvent(MyEvent2 event2) {
    // do something}}Copy the code

2. SubscriberRegistry. Java:

  // The clazz Class passed in is the Class of the Listener
  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()) {
        // See if the method has a Subscribe comment
        if(method.isAnnotationPresent(Subscribe.class) && ! method.isSynthetic()) {// TODO(cgdecker): Should check for a generic parameter type and error outClass<? >[] parameterTypes = method.getParameterTypes();// There is only one argument to check the method
          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());
  }
Copy the code

5.2. Construct EventBus

5.2.1 Instructions for Use

1. In a system, multiple EventBuses can exist at the same time based on different functions. Different EventBuses can be identified by their identifiers.

2. For ease of use, EventBus provides multiple constructors that users can inject into different implementation classes as needed. The simplest constructor is a no-parameter constructor that uses the default implementation.

3. In practice, you can use a singleton class to hold an EventBus instance, and if necessary, you can hold different EventBus instances for different purposes.

5.2.2 Relevant codes

1.EventBus.java

  // No parameter constructor
  public EventBus(a) {
    this("default");
  }
  
  // Specify the identifier constructor
  public EventBus(String identifier) {
    this(
        identifier,
        MoreExecutors.directExecutor(),
        Dispatcher.perThreadDispatchQueue(),
        LoggingHandler.INSTANCE);
  }
  
  // Inject a custom exception class constructor
  public EventBus(SubscriberExceptionHandler exceptionHandler) {
    this(
        "default",
        MoreExecutors.directExecutor(),
        Dispatcher.perThreadDispatchQueue(),
        exceptionHandler);
  }
  
  // Inject all argument constructors. Note that this method is not public and can only be accessed inside the package.
  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);
  }
Copy the code

5.3. Register the Listener

5.3.1 Instructions for Use

After the above two steps are complete, you can register the Listener through EventBus.

5.3.2 Relevant codes

1.EventBus.java

  private final SubscriberRegistry subscribers = new SubscriberRegistry(this);
  
  public void register(Object object) {
    subscribers.register(object);
  }
Copy the code

2.SubscriberRegistry.java

  void register(Object listener) {
    // Find a Subscribe method and encapsulate it as Subscriber. The Class of the key record of the Multimap is the Class of the object to listen onMultimap<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 = newCopyOnWriteArraySet<>(); eventSubscribers = MoreObjects.firstNonNull(subscribers.putIfAbsent(eventType, newSet), newSet); } eventSubscribers.addAll(eventMethodsInListener); }}Copy the code

5.4. Publish events

5.4.1 Instructions for Use

Publishing events is simple, as long as you get the EventBus instance where you want to publish the event, and then call the POST method.

5.4.2 Relevant codes

1.EventBus.java

  public void post(Object event) {
    // Find the subscriber based on the event.Class parameter of the Listener method.
    Iterator<Subscriber> eventSubscribers = subscribers.getSubscribers(event);
    if (eventSubscribers.hasNext()) {
      // The Subscriber dispatchEvent method is called
      dispatcher.dispatch(event, eventSubscribers);
    } else if(! (eventinstanceof DeadEvent)) {
      // the event had no subscribers and was not itself a DeadEvent
      post(new DeadEvent(this, event)); }}Copy the code

2.Subscriber.java

  final void dispatchEvent(final Object event) {
    // Implement asynchronous calls through executor, which is injectable in EventBus and can inject the modified implementation class
    executor.execute(
        new Runnable() {
          @Override
          public void run(a) {
            try {
              invokeSubscriberMethod(event);
            } catch (InvocationTargetException e) {
              / / here is the final call injection in the EventBus SubscriberExceptionHandler, can be injected into the revised implementation classbus.handleSubscriberException(e.getCause(), context(event)); }}}); }void invokeSubscriberMethod(Object event) throws InvocationTargetException {
    try {
      // Method is the Listener method, target is the Listener object, and event is the input parameter of the Listener method
      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();
      }
      throwe; }}Copy the code

6, Google Guava source address

Github.com/google/guav…

end.


HikariPool source code (a) first HikariPool source code (two) design ideas for reference


Java geek site: javageektour.com/