前言

EventBus的核心思想是观察者模式 (生产/消费者编程模型) 。

SpringBoot+EventBus使用教程(一)

SpringBoot+EventBus使用教程(二)

通过前面的文章我们已经知道,如何使用eventBus了。我们需要先定义一个Observer(前文中的EventListener类),然后将其注册到eventBus里,通过 @Subscribe 定义消息回调函数。

那我们先看看register(Object object) 和unregister(Object object) 方法。

register (Object object) 解析

 
1
2
3
4
5
6
7
8
9
10
public void register(Object object) {
    Multimap<Class<?>, EventSubscriber> methodsInListener =
        finder.findAllSubscribers(object);
    subscribersByTypeLock.writeLock().lock();
    try {
      subscribersByType.putAll(methodsInListener);
    } finally {
      subscribersByTypeLock.writeLock().unlock();
    }
  }

可以看到是先通过SubscriberFindingStrategy接口里的findAllSubscribers方法获取所有标记了@ Subscribe 注解的方法,其中该接口的具体实现是AnnotatedSubscriberFinder类。放到一个guava里定义的Multimap里。然后是把获取到的methodsInListener放到一个叫subscribersByType的 guava里定义的SetMultimap里 。

 
1
2
3
4
5
6
7
8
9
10
11
public Multimap<Class<?>, EventSubscriber> findAllSubscribers(Object listener) {
    Multimap<Class<?>, EventSubscriber> methodsInListener = HashMultimap.create();
    Class<?> clazz = listener.getClass();
    for (Method method : getAnnotatedMethods(clazz)) {
      Class<?>[] parameterTypes = method.getParameterTypes();
      Class<?> eventType = parameterTypes[0];
      EventSubscriber subscriber = makeSubscriber(listener, method);
      methodsInListener.put(eventType, subscriber);
    }
    return methodsInListener;
  }

findAllSubscribers方法里,最重要的是methodsInListener,它的结构可以简单理解为一个map,其中key是eventType,在我前文写的例子中就是com.sww.eventbus.domain.MessageEvent,其中value是subscriber,就是例子中的com.sww.eventbus.listener.EventListener#onMessageEvent。

总之,一句话就是先通过标记找到所有已经注册进来的观察者,然后存放到容器里备用。

那unregister就是从容器删除它们,

unRegister (Object object) 解析

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public void unregister(Object object) {
    Multimap<Class<?>, EventSubscriber> methodsInListener = finder.findAllSubscribers(object);
    for (Entry<Class<?>, Collection<EventSubscriber>> entry :
          methodsInListener.asMap().entrySet()) {
      Class<?> eventType = entry.getKey();
      Collection<EventSubscriber> eventMethodsInListener = entry.getValue();
 
      subscribersByTypeLock.writeLock().lock();
      try {
        Set<EventSubscriber> currentSubscribers = subscribersByType.get(eventType);
        if (!currentSubscribers.containsAll(eventMethodsInListener)) {
          throw new IllegalArgumentException(
              "missing event subscriber for an annotated method. Is " + object + " registered?");
        }
        currentSubscribers.removeAll(eventMethodsInListener);
      } finally {
        subscribersByTypeLock.writeLock().unlock();
      }
    }
  }

post( Object event)解析

有了观察者,下面就是发送事件了,阅读过前文会知道是通过eventBus.post(Object event)来发送事件消息。那咱们来看看这个post方法。

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public void post(Object event) {
    Set<Class<?>> dispatchTypes = flattenHierarchy(event.getClass());
 
    boolean dispatched = false;
    for (Class<?> eventType : dispatchTypes) {
      subscribersByTypeLock.readLock().lock();
      try {
        Set<EventSubscriber> wrappers = subscribersByType.get(eventType);
 
        if (!wrappers.isEmpty()) {
          dispatched = true;
          for (EventSubscriber wrapper : wrappers) {
            enqueueEvent(event, wrapper);
          }
        }
      } finally {
        subscribersByTypeLock.readLock().unlock();
      }
    }
 
    if (!dispatched && !(event instanceof DeadEvent)) {
      post(new DeadEvent(this, event));
    }
 
    dispatchQueuedEvents();
  }

该方法就是从之前的容器subscribersByType里获取到eventType对应的观察者,然后组装成EventWithSubscriber放到队列里。

 
1
2
3
void enqueueEvent(Object event, EventSubscriber subscriber) {
    eventsToDispatch.get().offer(new EventWithSubscriber(event, subscriber));
  }

然后就是最后的dispatchQueuedEvents(),经过一层层深入进去,可以发现wrapper.handleEvent(event),其中 handleEvent方法就是最终的关键了

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void handleEvent(Object event) throws InvocationTargetException {
    checkNotNull(event);
    try {
      method.invoke(target, new Object[] { 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;
    }
  }

就是通过Java的反射机制实现。

需要说明的是,如果没有订阅者注册到要发送的event事件上,并且该event不是DeadEvent,那么它将被包装成DeadEvent中并重新发布。也就是其中这三行代码索要做的

 
1
2
3
if (!dispatched && !(event instanceof DeadEvent)) {
      post(new DeadEvent(this, event));
    }

本文系本人原创,同步更新在我的独立博客http://791202.com/上,如要转载,请注明出处!

EventBus原理解析的更多相关文章

  1. [原][Docker]特性与原理解析

    Docker特性与原理解析 文章假设你已经熟悉了Docker的基本命令和基本知识 首先看看Docker提供了哪些特性: 交互式Shell:Docker可以分配一个虚拟终端并关联到任何容器的标准输入上, ...

  2. 【算法】(查找你附近的人) GeoHash核心原理解析及代码实现

    本文地址 原文地址 分享提纲: 0. 引子 1. 感性认识GeoHash 2. GeoHash算法的步骤 3. GeoHash Base32编码长度与精度 4. GeoHash算法 5. 使用注意点( ...

  3. Web APi之过滤器执行过程原理解析【二】(十一)

    前言 上一节我们详细讲解了过滤器的创建过程以及粗略的介绍了五种过滤器,用此五种过滤器对实现对执行Action方法各个时期的拦截非常重要.这一节我们简单将讲述在Action方法上.控制器上.全局上以及授 ...

  4. Web APi之过滤器创建过程原理解析【一】(十)

    前言 Web API的简单流程就是从请求到执行到Action并最终作出响应,但是在这个过程有一把[筛子],那就是过滤器Filter,在从请求到Action这整个流程中使用Filter来进行相应的处理从 ...

  5. GeoHash原理解析

    GeoHash 核心原理解析       引子 一提到索引,大家脑子里马上浮现出B树索引,因为大量的数据库(如MySQL.oracle.PostgreSQL等)都在使用B树.B树索引本质上是对索引字段 ...

  6. alibaba-dexposed 原理解析

    alibaba-dexposed 原理解析 使用参考地址: http://blog.csdn.net/qxs965266509/article/details/49821413 原理参考地址: htt ...

  7. 支付宝Andfix 原理解析

    支付宝Andfix 原理解析 使用参考地址: http://blog.csdn.net/qxs965266509/article/details/49802429 原理参考地址: http://blo ...

  8. JavaScript 模板引擎实现原理解析

    1.入门实例 首先我们来看一个简单模板: <script type="template" id="template"> <h2> < ...

  9. Request 接收参数乱码原理解析三:实例分析

    通过前面两篇<Request 接收参数乱码原理解析一:服务器端解码原理>和<Request 接收参数乱码原理解析二:浏览器端编码原理>,了解了服务器和浏览器编码解码的原理,接下 ...

随机推荐

  1. [.NET] 控制只启动单个指定外部程序

    独立观察员 2019 年 6 月 12 日 有的时候我们程序需要启动外部程序来配合实现某些功能,比如启动一个 Cef 相关程序来承载网页.那么如果那个外部程序并没有实现单例启动,我们程序去启动它的时候 ...

  2. 高性能TcpServer(C#) - 1.网络通信协议

    高性能TcpServer(C#) - 1.网络通信协议 高性能TcpServer(C#) - 2.创建高性能Socket服务器SocketAsyncEventArgs的实现(IOCP) 高性能TcpS ...

  3. js变量类型及检查

    一.变量的类型 JavaScript 有六种数据类型.主要的类型有 Number.String.object 以及 Boolean 类型,其他两种类型为 null 和 undefined.var ob ...

  4. 1、Ext.NET 1.7官方示例笔记-事件

    官网参考地址:https://examples1.ext.net/#/Events/DirectEvents/Overview/ 先了解一下“事件” Ext.NET包括3种事件机制 DirectEve ...

  5. 纯C语言实现顺序栈

    #include <stdio.h> #include <stdlib.h> #define MAXSIZE 100 typedef int SElemType; typede ...

  6. curl sftp libcurl 功能使用

    #include <curl/curl.h> #undef DISABLE_SSH_AGENT struct FtpFile { const char *filename; FILE *s ...

  7. Qt压缩和解压 zip

    zlib编译详见 https://blog.csdn.net/zhangxuechao_/article/details/85049711 下载quazip https://github.com/st ...

  8. C# 测试网络速度例子

    using System.Net.NetworkInformation; namespace PingExample { public partial class Form1 : Form { pub ...

  9. Linux 多线程应用中如何编写安全的信号处理函数【转】

    转自:https://www.cnblogs.com/virusolf/p/4945642.html http://blog.163.com/he_junwei/blog/static/1979376 ...

  10. Python微信公众号开发—小白篇(1)

    本文面向想通过Python学习公众号开发的同学.一站式解决新手开发微信公众号遇到的所有问题. 为了防止我的文章被到处转载,贴一下我的公众号[智能制造社区],欢迎大家关注. github仓库地址http ...