观察者模式设计-Guava EventBus源码分析

EventBus源码分析

观察者模式

观察者模式(Observer Design Pattern)也被称为发布订阅模式(Publish-Subscribe Design Pattern)。在 GoF 的《设计模式》一书中,它的定义是这样的:

Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

翻译成中文就是:在对象之间定义一个一对多的依赖,当一个对象状态改变的时候,所有依赖的对象都会自动收到通知。

一般情况下,被依赖的对象叫作被观察者(Observable),依赖的对象叫作观察者(Observer)。不过,在实际的项目开发中,这两种对象的称呼是比较灵活的,有各种不同的叫法,比如:Subject-Observer、Publisher-Subscriber、Producer-Consumer、EventEmitter-EventListener、Dispatcher-Listener。不管怎么称呼,只要应用场景符合刚刚给出的定义,都可以看作观察者模式

Guava提供同步和异步观察者模式的实现,分别是EventBus, AsyncEventBus

版本:29.0-jre

使用方式非常简单,Example:

public class EventBusExample {

  private static final String EVENT = "Hello";
  private EventBus eventBus = new EventBus();

  @Test
  public void testEventBus() {
      StringCatcher catcher = new StringCatcher();
      // 注册一个观察者对象, 其中@Subscribe标记的方法,第一个参数类型即作为被监听的事件
      eventBus.register(catcher);

      // 提交一个事件, 根据event类型,从subscribe映射表中找出所有@Subscribe的订阅者,
      // 然后所有订阅者都处理这些事件
      eventBus.post(EVENT);

      assertEquals(1, catcher.events.size());
      assertEquals(EVENT, catcher.events.get(0));
  }

  public static class StringCatcher {

      private List events = Lists.newArrayList();

      @Subscribe
      public void hereHaveAString(@Nullable String string) {
          events.add(string);
      }
      
      public void methodWithoutAnnotation(@Nullable String string) {
          fail("Event bus must not call methods without @Subscribe!");
      }

      public List getEvents() {
          return events;
      }
      
  }
  
}

原理分析

实现原理:

  • 带有@Subscribe注解标记的方法作为被观察的方法,第一个参数类型作为被观察的事件类型,每次注册一个观察者时都会将<事件类型, Subscribe>解析(包含封装)出来
    提交的事件根据这个subscribers映射表,找到指定事件的Subscribe集合,然后将事件分发给这些Subscribe也就是订阅者

核心组件有

  • EventBus同步阻塞、AsyncEventBus异步非阻塞 使用入口类,可以注册listener对象,post(event), unregister等等
  • SubscriberRegistry,维护subscriber订阅表
  • Dispatcher,事件分发器,Guava提供三种实现:
    • PerThreadQueuedDispatcher,通过ThreadLocal来将线程间events分隔开处理,内部维护一个队列,也算是层次性遍历的方式(也就是广度优先遍历, 如果有子类事件 需要等上层所有事件执行完毕才执行,将子事件放置队尾即可),因此自然不支持重入性
    • LegacyAsyncDispatcher,异步处理events
    • ImmediateDispatcher,直接分发事件,内部不维护队列,类似于深度优先遍历

SubscriberRegistry

register 注册listener对象

这里有个小技巧,subscribers映射表使用了CopyOnWriteArraySet集合,在多线程的情况下,写时复制,也就是在写入数据的时候,
会创建一个新的 set,并且将原始数据 clone 到新的 set 中,在新的 set 中写入数据完成之后,再用新的 set 替换老的 set。这样就能保证在写入数据的时候,不影响数据的读取操作,以此来解决读写并发问题

// SubscriberRegistry#subscribers参数
// private final ConcurrentMap, CopyOnWriteArraySet> subscribers =
         Maps.newConcurrentMap(); // 整个注册和事件处理都是基于此注册表进行处理

void register(Object listener) {
  // 找到该listener的事件和Subscriber映射关系
  // <事件类型, Subscriber>注册表
  Multimap, Subscriber> listenerMethods = findAllSubscribers(listener);

  for (Entry, Collection> entry : listenerMethods.asMap().entrySet()) {
    Class eventType = entry.getKey();
    Collection eventMethodsInListener = entry.getValue();

    CopyOnWriteArraySet eventSubscribers = subscribers.get(eventType);

    if (eventSubscribers == null) {
      CopyOnWriteArraySet newSet = new CopyOnWriteArraySet<>();
      eventSubscribers =
          MoreObjects.firstNonNull(subscribers.putIfAbsent(eventType, newSet), newSet);
    }

    // 将注册信息维护到subscribers表中
    eventSubscribers.addAll(eventMethodsInListener);
    
  }
  
}
Unregisters
/** Unregisters all subscribers on the given listener object. */
void unregister(Object listener) {
  Multimap, Subscriber> listenerMethods = findAllSubscribers(listener);

  // 和注册过程相反,从subscribers表中删除该listener的Subscriber相关信息
  for (Entry, Collection> entry : listenerMethods.asMap().entrySet()) {
    Class eventType = entry.getKey();
    Collection listenerMethodsForType = entry.getValue();

    CopyOnWriteArraySet currentSubscribers = subscribers.get(eventType);
    if (currentSubscribers == null || !currentSubscribers.removeAll(listenerMethodsForType)) {
      // if removeAll returns true, all we really know is that at least one subscriber was
      // removed... however, barring something very strange we can assume that if at least one
      // subscriber was removed, all subscribers on listener for that event type were... after
      // all, the definition of subscribers on a particular class is totally static
      throw new IllegalArgumentException(
          "missing event subscriber for an annotated method. Is " + listener + " registered?");
    }

    // don't try to remove the set if it's empty; that can't be done safely without a lock
    // anyway, if the set is empty it'll just be wrapping an array of length 0
    
  }
  
}    
findAllSubscribers

找出指定listener中的订阅者信息(@Subscribe注解的方法)

private Multimap, Subscriber> findAllSubscribers(Object listener) {
  // @Subscribe注解的方法,第一个参数表示可以响应的事件类型
  // 因此这里的映射关系是
  Multimap, Subscriber> methodsInListener = HashMultimap.create();
  Class clazz = listener.getClass();
  // 这里就是通过反射拿到有@Subscriber注解的方法
  for (Method method : getAnnotatedMethods(clazz)) {
    Class[] parameterTypes = method.getParameterTypes();
    Class eventType = parameterTypes[0];
    methodsInListener.put(eventType, Subscriber.create(bus, listener, method));
  }
  return methodsInListener;
}

这里有个小技巧,在通过反射拿到有@Subscriber注解的方法时会用一个缓存,来保存的信息
比如创建多个EventBus实例 注册相同的listener时,就不用在通过反射拿到这种对应关系,提高效率

 private static ImmutableList getAnnotatedMethods(Class clazz) {
   return subscriberMethodsCache.getUnchecked(clazz);
 }
 
/**
 * A thread-safe cache that contains the mapping from each class to all methods in that class and
 * all super-classes, that are annotated with {@code @Subscribe}. The cache is shared across all
 * instances of this class; this greatly improves performance if multiple EventBus instances are
 * created and objects of the same class are registered on all of them.
 */
private static final LoadingCache, ImmutableList> subscriberMethodsCache =
    CacheBuilder.newBuilder()
        // 这里key用的弱引用,也就是每次GC不管内存是否足够都会回收掉,
        // key被回收掉,相应的记录也就会被删除,下次取数据时需要重新加载
        .weakKeys()
        .build(
            new CacheLoader, ImmutableList>() {
              @Override
              public ImmutableList load(Class concreteClass) throws Exception {
                return getAnnotatedMethodsNotCached(concreteClass);
              }
            });
            
            
private static ImmutableList getAnnotatedMethodsNotCached(Class clazz) {
  Set> supertypes = TypeToken.of(clazz).getTypes().rawTypes();
  Map 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();
        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());

}    
getSubscribers

从subscribers表中找到指定事件的订阅者集合

Iterator getSubscribers(Object event) {
  // 先把事件类型平铺开(父类等信息),当然这里也用的是缓存来优化
  ImmutableSet> eventTypes = flattenHierarchy(event.getClass());

  List> subscriberIterators =
      Lists.newArrayListWithCapacity(eventTypes.size());

  // 这里返回的subscribers不仅包含event类型的订阅者,也包含event父类的订阅者
  for (Class eventType : eventTypes) {
    CopyOnWriteArraySet eventSubscribers = subscribers.get(eventType);
    if (eventSubscribers != null) {
      // eager no-copy snapshot
      subscriberIterators.add(eventSubscribers.iterator());
    }
  }

  return Iterators.concat(subscriberIterators.iterator());
}

static ImmutableSet> flattenHierarchy(Class concreteClass) {
  try {
    return flattenHierarchyCache.getUnchecked(concreteClass);
  } catch (UncheckedExecutionException e) {
    throw Throwables.propagate(e.getCause());
  }
}  

/** Global cache of classes to their flattened hierarchy of supertypes. */
private static final LoadingCache, ImmutableSet>> flattenHierarchyCache =
    CacheBuilder.newBuilder()
        .weakKeys()
        .build(
            new CacheLoader, ImmutableSet>>() {
              // > is actually needed to compile
              @SuppressWarnings("RedundantTypeArguments")
              @Override
              public ImmutableSet> load(Class concreteClass) {
                return ImmutableSet.>copyOf(
                    TypeToken.of(concreteClass).getTypes().rawTypes());
              }

            });   

Dispatcher 事件分发器

对于通过eventBus实例post的event,guava提供了几种事件转发策略,EventBus默认是PerThreadQueuedDispatcher,线程间隔离(ThreadLocal实现)

PerThreadQueuedDispatcher

 private static final class PerThreadQueuedDispatcher extends Dispatcher {

   /** Per-thread queue of events to dispatch. */
   private final ThreadLocal> queue =
       new ThreadLocal>() {
         @Override
         protected Queue initialValue() {
           return Queues.newArrayDeque();
         }
       };

   /** Per-thread dispatch state, used to avoid reentrant event dispatching. */
   private final ThreadLocal dispatching =
       new ThreadLocal() {
         @Override
         protected Boolean initialValue() {
           return false;
         }
       };

   @Override
   void dispatch(Object event, Iterator subscribers) {
     checkNotNull(event);
     checkNotNull(subscribers);
     Queue queueForThread = queue.get();
     // 这里将event和subscribers集合封装成一个事件
     queueForThread.offer(new Event(event, subscribers));

     // 避免重入,也就是在处理某一个事件的时候,事件内部又通过eventBus实例post事件
     // 这个时候就只能放在queue里等待按序处理了
     // 重入性相关讨论:https://stackoverflow.com/questions/21947936/guava-eventbus-dispatching
     if (!dispatching.get()) {
       dispatching.set(true);
       try {
         Event nextEvent;
         while ((nextEvent = queueForThread.poll()) != null) {
           while (nextEvent.subscribers.hasNext()) {
             // 将事件相关的订阅者都挨个进行"通知"
             nextEvent.subscribers.next().dispatchEvent(nextEvent.event);
           }
         }
       } finally {
         dispatching.remove();
         queue.remove();
       }
     }
   }

   private static final class Event {
     private final Object event;
     private final Iterator subscribers;

     private Event(Object event, Iterator subscribers) {
       this.event = event;
       this.subscribers = subscribers;
     }
   }
 }           
 
// 这里就是事件通知了,executor可以定义成同步的(比如guava的MoreExecutors.directExecutor())
// 要想实现异步,定义成异步的就行
final void dispatchEvent(final Object event) {
  executor.execute(
      new Runnable() {
        @Override
        public void run() {
          try {
            invokeSubscriberMethod(event);
          } catch (InvocationTargetException e) {
            bus.handleSubscriberException(e.getCause(), context(event));
          }
        }
        
      });

}
LegacyAsyncDispatcher

异步事件分发器,定义了一个支持多线程安全的全局队列,异步非阻塞需要 executor 来实现
AsyncEventBus就是以这种分发器来做事件分发处理的

  • 多线程环境 事件放进队列可能并非有序(注意这里是将event和单个subscriber封装成一个事件放进队列),
    比如A事件在一个线程(3个subscriber),B事件在一个线程(2个subscriber),组合可能有[a1, a2, a3, b1, b2], [a1, b2, a2, a3, b2], [a1, b2, b3, a2, a3], etc.
  • 事件分发顺序可能和事件添加到队列的顺序不一致, 比如first线程先从队列里取一个事件,second线程后从队列里取一个事件,second线程先进行事件分发
private static final class LegacyAsyncDispatcher extends Dispatcher {

  /** Global event queue. */
  private final ConcurrentLinkedQueue queue =
      Queues.newConcurrentLinkedQueue();

  @Override
  void dispatch(Object event, Iterator subscribers) {
    checkNotNull(event);
    while (subscribers.hasNext()) {
      // 这里是将event和单个subscriber封装成一个事件
      queue.add(new EventWithSubscriber(event, subscribers.next()));
    }

    EventWithSubscriber e;
    while ((e = queue.poll()) != null) {
      e.subscriber.dispatchEvent(e.event);
    }
  }

  private static final class EventWithSubscriber {
    private final Object event;
    private final Subscriber subscriber;

    private EventWithSubscriber(Object event, Subscriber subscriber) {
      this.event = event;
      this.subscriber = subscriber;
    }
    
  }
  
}  
ImmediateDispatcher

这个比较简单,就是post一个事件就直接分发出去(深度优先分发),传统观察者模式也就是这种方式

private static final class ImmediateDispatcher extends Dispatcher {
  private static final ImmediateDispatcher INSTANCE = new ImmediateDispatcher();

  @Override
  void dispatch(Object event, Iterator subscribers) {
    checkNotNull(event);
    while (subscribers.hasNext()) {
      subscribers.next().dispatchEvent(event);
    }
    
  }
  
}  

EventBus

EventBus 实现的是同步阻塞的观察者模式。代码上虽然用了线程池 Executor,但实际上,MoreExecutors.directExecutor() 是 Google Guava 提供的工具类,看似是多线程,实际上是单线程。
之所以要这么实现,主要还是为了跟 AsyncEventBus 统一代码逻辑,做到代码复用

private final String identifier;
private final Executor executor;
private final SubscriberExceptionHandler exceptionHandler;

private final SubscriberRegistry subscribers = new SubscriberRegistry(this);
private final Dispatcher dispatcher;

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);
}

post方法

 public void post(Object event) {
   Iterator eventSubscribers = subscribers.getSubscribers(event);
   if (eventSubscribers.hasNext()) {
     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));
   }
 }  

register方法

public void register(Object object) {
  subscribers.register(object);
}     

AsyncEventBus

继承自EventBus,除了Dispatcher使用LegacyAsyncDispatcher外,其他和EventBus一致
为了实现异步非阻塞的观察者模式,它就不能再继续使用 MoreExecutors.directExecutor() 了,而是需要在构造函数中,由调用者注入线程池

public class AsyncEventBus extends EventBus {

 public AsyncEventBus(String identifier, Executor executor) {
   super(identifier, executor, Dispatcher.legacyAsync(), LoggingHandler.INSTANCE);
 }

 public AsyncEventBus(Executor executor, SubscriberExceptionHandler subscriberExceptionHandler) {
   super("default", executor, Dispatcher.legacyAsync(), subscriberExceptionHandler);
 }

 public AsyncEventBus(Executor executor) {
   super("default", executor, Dispatcher.legacyAsync(), LoggingHandler.INSTANCE);
 }
 
    }    

以上是个人理解,如果问题请指出,谢谢!

你可能感兴趣的:(JAVA)