前言

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. 升级 ASP.NET Core 3.0 设置 JSON 返回 PascalCase 格式与 SignalR 问题

    由于一些 JS 组件要求 JSON 格式是 PascalCase 格式,新版本 ASP.NET Core 3.0 中默认移除了 Newtonsoft.Json ,使用了微软自己实现的 System.T ...

  2. SpringBoot整合kafka(实现producer和consumer)

    本文代码使用的是Spring Boot 2.1.8.RELEASE 版本 <parent> <groupId>org.springframework.boot</grou ...

  3. OpenGL入门1.2:渲染管线简介,画三角形

    每一个小步骤的源码都放在了Github 的内容为插入注释,可以先跳过 图形渲染管线简介 在OpenGL的世界里,任何事物是处于3D空间中的,而屏幕和窗口显示的却是2D,所以OpenGL干的事情基本就是 ...

  4. redis笔记1

    存储结构 字符类型 散列类型 列表类型 集合类型 有序集合 可以为每个key设置超时时间: 可以通过列表类型来实现分布式队列的操作 支持发布订阅的消息模式 提供了很多命令与redis进行交互 数据缓存 ...

  5. Linux设备管理(四)_从sysfs回到ktype【转】

    转自:https://www.cnblogs.com/xiaojiang1025/archive/2016/12/21/6202298.html sysfs是一个基于ramfs的文件系统,在2.6内核 ...

  6. Ubuntu下搭建Kubernetes集群(2)--docker基本操作

    查看当前的容器和images docker ps -a docker images 1.创建新的容器 docker run -it --name 容器名 镜像名 /bin/bash # 挂载目录和端口 ...

  7. HBuilder创建app

    一.app登录注册实现 1.首先进行布局,mhead,mbody 在app index.html内创建一个 a链接通过mui.openWindow跳到登录页面 <a class="mu ...

  8. Nginx的负载均衡配置(七)

    原文链接:https://www.cnblogs.com/knowledgesea/p/5199046.html 首先给大家说下upstream这个配置的,这个配置是写一组被代理的服务器地址,然后配置 ...

  9. vscode使用插件来添加文件说明和函数说明——42header——psioniq File Header——koroFileHeader

    安装号以后,设置快捷键如下: 同时需要根据自己的需要的修改json文件 // 文件头部注释 "fileheader.customMade": { "Description ...

  10. 【excel】=EXACT(A1,B1) 比较两个字符串是否相等

    相等返回true 不相等返回false