EventBus源码学习笔记(一)

EventBus 深入学习一

EventBus是一个消息总线,以观察者模式实现,用于简化程序的组件、线程通信,可以轻易切换线程、开辟线程; 传统上,Java的进程内事件分发都是通过发布者和订阅者之间的显式注册实现的。设计EventBus就是为了取代这种显示注册方式,使组件间有了更好的解耦。EventBus不是通用型的发布-订阅实现,不适用于进程间通信

开始之前,我们可以先想一下,什么东西是发布-订阅模型,如果要让我们自己设计一个发布-订阅模型的框架,要怎么处理

举一个小例子,谈一下我的理解

有一个存钱罐,长辈向里面放钱(1毛,5毛,1元,5元,10元,20元,50元,100元), 晚辈从里面取钱;与现实有点不同的是,每个长辈只能投相同面额的钱,晚辈只能获取相同面额的钱 假设小红,小明,小刚三个人是取10元钱的,现在张三放了10元钱, 然后主动把这10元钱给这三个人,不管他们去干嘛(与现实不同的是,张三只防了10元钱,但是小红,小明,小刚都拿到了这10元钱)

接本的业务流程如下:

  • 三个角色,发布消息者(长辈),存储消息的管道(存钱罐子),订阅者(晚辈);
  • 发布者将发送消息到消息管道
  • 管道则将消息推送给订阅者

设计:

  • 发布者: 任何发布消息的人
  • 消息管道: 连接发布者和订阅者的桥梁,主要有两个功能,一是接受发布者发布的消息;而是将消息推送给订阅者
    • 维护一个订阅者关系(消息->订阅者), 因此需要开放给订阅者一个注册的接口
    • 接受发布者发送消息, 因此需要开放给发布者一个发布消息的接口
    • 推送消息给订阅者, (这里调用订阅者提供的消息接受回调方法,实现消息推送)
  • 订阅者: 每个订阅者,关注并处理一种消息类型,
    • 想成为一个订阅者,先到消息管道这里注册,(告知消费信息的类型以及接受消息的回调方法)
    • 接受消息的回调方法(即接受消息后执行业务逻辑的主体)

使用

使用非常简单, 创建一个 EventBus 实例, 订阅方,调用 EventBus.register() 方法注册, 消息发布方,调用eventBus.post(event); 来发布消息, 则订阅类中,添加 @subscribe 注解的方法将会接受到这条消息

实例如下:

/**
 * 发送的消息
 */
@ToString
@Getter
@Setter
public class AuditEvent {

    /**
     * 审核人
     */
    private String auditUser;


    /**
     * 审核记录
     */
    private String record;


    /**
     * 审核结果
     */
    private AuditEventEnum auditResultEnum;


    public enum AuditEventEnum {

        ACCEPT,

        REJECT,

        RETRY;

    }
}

订阅者

/**
 * 初审 & 复审的监听器
 * <p/>
 * Created by yihui on 2017/3/1.
 */
@Component
public class AuditEventListener {

    private static final Logger logger = LoggerFactory.getLogger(AuditEventListener.class);


    /**
     * 注册事件
     */
    @PostConstruct
    public void init() {
        ActionEventBus.register(this);
    }


    /**
     * 审核完成后,会发送一条消息
     * <p/>
     * 1. 通过审核
     * 2. 拒绝审核
     * 3. 重新审核
     * <p/>
     * 根据消息, 做出不同的action
     *
     * @param args
     */
    @Subscribe
    public void invoke(AuditEvent args) {

        if (args.getAuditResultEnum() == AuditEvent.AuditEventEnum.ACCEPT) {
            System.out.println(1111);
            logger.info("审核通过!!!! {}", args.getRecord());
        } else if (args.getAuditResultEnum() == AuditEvent.AuditEventEnum.REJECT) {
            System.out.println(2222);
            logger.info("审核拒绝!!!! {}", args.getRecord());
        } else {
            logger.info("重新审核!!!! {}", args.getRecord());
        }

    }
}

EventBus管理工厂

public class ActionEventBus {

    private final static EventBus eventBus = new EventBus();


    public static void post(Object event) {
        eventBus.post(event);
    }

    public static void register(Object handler) {
        eventBus.register(handler);
    }

    public static void unregister(Object handler) {
        eventBus.unregister(handler);
    }

}

发布消息

 @Test
public void testAudit() {
   AuditEvent auditEvent = new AuditEvent();
   auditEvent.setAuditResultEnum(AuditEvent.AuditEventEnum.ACCEPT);
   auditEvent.setAuditUser("1hui");
   auditEvent.setRecord("test1");

   // 发布一条成功的消息
   ActionEventBus.post(auditEvent);



   auditEvent.setAuditResultEnum(AuditEvent.AuditEventEnum.REJECT);
   auditEvent.setAuditUser("2hui");
   auditEvent.setRecord("test2");
   // 发布一条拒绝的消息
   ActionEventBus.post(auditEvent);




   BuyEvent buyEvent = new BuyEvent();
   buyEvent.setBuyerUser("3hui");
   buyEvent.setCount(1);
   buyEvent.setTotalPrice(10000L);
   buyEvent.setItem("java book");
   buyEvent.setBuyEventEnum(BuyEventEnum.PAY);
   ActionEventBus.post(buyEvent);
   System.out.println("over");
}

分析

说明,我们先拿google的 Guava中的 EventBus 来作为研究对象; 后续会对比下android平台上使用非常多的greenrobot/EventBus

从上面的使用可以简单的看出EventBus的设计思路基本上还是 消息-订阅的模子,但是设计非常巧妙

从订阅者角度来看,首先是要注册,没什么好说的,关键点就在于接受消息的处理方法上

- 添加一个注解,指定消息接收类型(即参数类型), 就可以接受这类消息
- 基于上面的方法,一个订阅者,可以实现订阅多个不同的消息源

消息发布方来看,直接调用 EventBus.post() 就算是发布消息,使用起来超级简单

其中 EventBus 作为沟通的桥梁,也就是上面我们说的‘储钱罐’, 如果希冀实现异步的消息处理,则直接用AsyncEventBus 即可

从上面的使用来看,极大的简化了使用的流程,简直不能更easy了; 唯一的遗憾是,从上面的描述中,发现使用异步的话,还得改用AsyncEventBus 有点麻烦,如果能直接再 @subscribe 注解中添加个标识,表示是否使用异步消费就完美了


前期准备

在真正进入源码分析之前,我们先做些准备工作,了解下基本的术语和背景

1. 事件监听者(Listeners)

即我们上面的订阅者,最终接受事件,并执行响应的业务逻辑的主体

在EventBus实例上调用EventBus.register(Object)方法注册事件监听者;需要注意的是请保证事件生产者和监听者共享相同的EventBus实例

2. 事件生产者(Producers)

发送事件的主体,通过把事件传递给 EventBus.post(Object)方法。异步分发可以直接用EventBus的子类AsyncEventBus

3. 术语

术语

说明

事件

可以向事件总线发布的对象

订阅

向事件总线注册监听者以接受事件的行为

监听者

提供一个处理方法,希望接受和处理事件的对象

处理方法

监听者提供的公共方法,事件总线使用该方法向监听者发送事件;该方法应该用Subscribe注解

发布消息

通过事件总线向所有匹配的监听者提供事件

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Seebug漏洞平台

TP-LINK WR941N路由器研究

是TP-Link WR940N后台的RCE, 手头上正好有一个TP-Link WR941N的设备,发现也存在相同的问题,但是CVE-2017-13772文章中给...

4168
来自专栏Spark学习技巧

深入了解HBase架构

2012
来自专栏京东技术

开发属于自己的插件 | IDEA &amp; Android Studio插件开发指南

谷轩宇——从事安卓开发,目前效力于通天塔技术开放组是否曾经被ide重复繁琐的操作所困扰,又或者没有心仪的UI控件而难受。那么请阅读这篇文章,掌握idea插件的开...

8152
来自专栏转载gongluck的CSDN博客

Brpc学习:简单回显服务器/客户端

sudo apt-get install git g++ make libssl-dev sudo apt-get install realpath libgf...

2.1K6
来自专栏蘑菇先生的技术笔记

那些年我们一起追过的缓存写法(四)

2846
来自专栏CSDN技术头条

一组 Redis 实际应用中的异常场景及其根因分析和解决方案

在上一场 Chat《基于 Redis 的分布式缓存实现方案及可靠性加固策略》中,我已经较为全面的介绍了 Redis 的原理和分布式缓存方案。如果只是从“会用”的...

3593
来自专栏Seebug漏洞平台

TP-LINK WR941N路由器研究

作者:Hcamael@知道创宇404实验室 之前看到了一个CVE, CVE-2017-13772 是TP-Link WR940N后台的RCE, 手头上正好有一个...

3176
来自专栏匠心独运的博客

消息中间件—RabbitMQ(集群监控篇1)

摘要:任何没有监控的系统上线,一旦在生产环境发生故障,那么排查和修复问题的及时性将无法得到保证

2673
来自专栏Seebug漏洞平台

Joomla 权限提升漏洞(CVE-2016-9838)分析

0x00 漏洞概述 1.漏洞简介 Joomla 于12月13日发布了3.6.5的升级公告,此次升级修复了三个安全漏洞,其中 CVE-2016-9838 被官方定...

33810
来自专栏北京马哥教育

深入浅出:Linux设备驱动之中断与定时器

“我叮咛你的 你说 不会遗忘 你告诉我的 我也全部珍藏 对于我们来说 记忆是飘不落的日子 永远不会发黄 相聚的时候 总是很短 期待的时候 总是很长 岁月的溪水边...

3969

扫码关注云+社区

领取腾讯云代金券