文档章节

Guava库学习:学习Guava EventBus(二)EventBus 事件订阅示例

Realfighter
 Realfighter
发布于 2015/04/25 17:33
字数 2885
阅读 2677
收藏 15
点赞 1
评论 1

    原文地址:Guava库学习:学习Guava EventBus(二)EventBus 事件订阅示例

    上一篇Guava库学习:学习Guava EventBus(一)EventBus,我们简单的对Guava基于事件的编程进行了介绍,学习和了解了EventBus类的使用,本篇起,我们通过一系列的示例代码深入的学习EventBus类,本篇学习Guava EventBus(二)EventBus 事件订阅示例。

    订阅Subscribe

    首先,我们假定定义了如下所示的TradeAccountEvent类,如下:

public class TradeAccountEvent {
    private double amount;
    private Date tradeExecutionTime;
    private TradeType tradeType;
    private TradeAccount tradeAccount;
    public TradeAccountEvent(TradeAccount account, double amount,
                             Date tradeExecutionTime, TradeType tradeType) {
        checkArgument(amount > 0.0, "Trade can't be less than zero");
        this.amount = amount;
        this.tradeExecutionTime =
                checkNotNull(tradeExecutionTime, "ExecutionTime can't be null");
        this.tradeAccount = checkNotNull(account, "Account can't be null ");
        this.tradeType = checkNotNull(tradeType, "TradeType can't be null ");
    }
    //Details left out for clarity
}

    每当执行买卖交易时,我们都将创建TradeAccountEvent类的一个实例。现在,假定我们有一个需要审计的交易,并且正在执行,所以我们需要有这样一个SimpleTradeAuditor类,如下所示:

    public class SimpleTradeAuditor {
        private List<TradeAccountEvent> tradeEvents =
                Lists.newArrayList();
        public SimpleTradeAuditor(EventBus eventBus) {
            eventBus.register(this);
        }
        @Subscribe
        public void auditTrade(TradeAccountEvent tradeAccountEvent) {
            tradeEvents.add(tradeAccountEvent);
            System.out.println("Received trade " + tradeAccountEvent);
        }
    }

    这里简单的分析一下上面的代码。在SimpleTradeAuditor的构造方法中,我们接收了EventBus类的一个实例,并且通过EventBus立即注册了SimpleTradeAuditor类,来接收TradeAccountEvents类的通知。我们通过在auditTrade方法上添加@Subscribe注解,来指定auditTrade作为事件处理方法。在上面的例子中,我们只是简单的把TradeAccountEvent对象添加到一个list,并简单的输出到控制台。

    发布Publishing

    首先来看下面的示例代码,代码如下:

    public class SimpleTradeExecutor {
        private EventBus eventBus;
        public SimpleTradeExecutor(EventBus eventBus) {
            this.eventBus = eventBus;
        }
        public void executeTrade(TradeAccount tradeAccount, double
                amount, TradeType tradeType) {
            TradeAccountEvent tradeAccountEvent =
                    processTrade(tradeAccount, amount, tradeType);
            eventBus.post(tradeAccountEvent);
        }
        private TradeAccountEvent processTrade(TradeAccount
                                                       tradeAccount, double amount, TradeType tradeType) {
            Date executionTime = new Date();
            String message = String.format("Processed trade for %s of amount %n type %s @%s", tradeAccount, amount, tradeType, executionTime);
            TradeAccountEvent tradeAccountEvent = new TradeAccountEvent(tradeAccount, amount, executionTime, tradeType);
            System.out.println(message);
            return tradeAccountEvent;
        }
    }

    与SimpleTradeAuditor类相似,我们也通过EventBus实例,构造了SimpleTradeExecutor。但是与SimpleTradeAuditor类不同的是,我们保存了一份EventBus的引用以备后用。你可能看到过很多类似的编码,对于相同实例在两个类之间的传递,这是至关重要的。在以后的示例中,我们将会介绍使用多个EventBus实例,在本篇的例子中, 我们使用单个EventBus实例。

    在上面的例子中,SimpleTradeExecutor类,有一个公共的executeTrade方法,它接收了用来处理交易所需要的所有信息。我们调用processTrade方法传递所需的信息,当执行交易的时候打印信息到控制台,然后返回一个TradeAccountEvent实例。当processTrade方法执行完成,我们调用EventBus.post()方法发布返回的TradeAccountEvent实例,并通知所有TradeAccountEvent对象的订阅者。如果我们快速的比较下SimpleTradeAuditor和SimpleTradeExecutor类,我们看到,虽然两个类都参与共享所需的信息,但是它们彼此之间没有任何的耦合。

    更细粒度的订阅

    上面我们看到了使用EventBus类进行发布和订阅的简单例子,EventBus基于类型发布事件,这些类型被订阅的方法接受。这让我们能够灵活的将事件发送给不同的用户类型。例如,我们需要单独的进行买卖交易的审计。首先,我们需要创建两个不同类型的事件。

    public class SellEvent extends TradeAccountEvent {
        public SellEvent(TradeAccount tradeAccount, double amount, Date
                tradExecutionTime) {
            super(tradeAccount, amount, tradExecutionTime, TradeType.SELL);
        }
    }
    public class BuyEvent extends TradeAccountEvent {
        public BuyEvent(TradeAccount tradeAccount, double amount, Date
                tradExecutionTime) {
            super(tradeAccount, amount, tradExecutionTime, TradeType.BUY);
        }
    }

    现在我们已经创建了两个离散事件类:SellEvent和BuyEvent,他们都继承了TradeAccountEvent类。为了实现单独的审计,我们首先为审计SellEvent类创建一个实例:

    public class TradeSellAuditor {
        private List<SellEvent> sellEvents = Lists.newArrayList();
        public TradeSellAuditor(EventBus eventBus) {
            eventBus.register(this);
        }
        @Subscribe
        public void auditSell(SellEvent sellEvent){
            sellEvents.add(sellEvent);
            System.out.println("Received SellEvent "+sellEvent);
        }
        public List<SellEvent> getSellEvents() {
            return sellEvents;
        }
    }

    我们看到,上面的TradeSellAuditor非常类似于SimpleTradeAuditor,不过TradeSellAuditor 只会接收SellEvent实例。接下来,我们创建一个只审计BuyEvent类的实例:

    public class TradeBuyAuditor {
        private List<BuyEvent> buyEvents = Lists.newArrayList();
        public TradeBuyAuditor(EventBus eventBus) {
            eventBus.register(this);
        }
        @Subscribe
        public void auditBuy(BuyEvent buyEvent){
            buyEvents.add(buyEvent);
            System.out.println("Received TradeBuyEvent "+buyEvent);
        }
        public List<BuyEvent> getBuyEvents() {
            return buyEvents;
        }
    }

    下面,我们简单的修改一下SimpleTradeExecutor类的代码,使其能够根据交易的类型来创建正确的TradeAccountEvent实例,代码如下:

   public class BuySellTradeExecutor {
        private EventBus eventBus;
        public BuySellTradeExecutor(EventBus eventBus) {
            this.eventBus = eventBus;
        }
        public void executeTrade(TradeAccount tradeAccount, double
                amount, TradeType tradeType) {
            TradeAccountEvent tradeAccountEvent =
                    processTrade(tradeAccount, amount, tradeType);
            eventBus.post(tradeAccountEvent);
        }
        private TradeAccountEvent processTrade(TradeAccount
                                                       tradeAccount, double amount, TradeType tradeType) {
            Date executionTime = new Date();
            String message = String.format("Processed trade for %s of amount %n type %s @%s", tradeAccount, amount, tradeType, executionTime);
            TradeAccountEvent tradeAccountEvent;
            if (tradeType.equals(TradeType.BUY)) {
                tradeAccountEvent = new BuyEvent(tradeAccount, amount,
                        executionTime);
            } else {
                tradeAccountEvent = new SellEvent(tradeAccount,
                        amount, executionTime);
            }
            System.out.println(message);
            return tradeAccountEvent;
        }
    }

    这样我们就已经创建了一个新的BuySellTradeExecutor类,根据交易的类型,我们将创建相应的BuyEvent或SellEvent实例,它的作用与我们之前的SimpleTradeExecutor类相似。但是,EventBus类是完全没有意识到这些变化的。我们注册了不同的订阅者并发布了不同的事件,这些变化对EventBus类来说是透明的。

    注意,我们不需要为这些事件的通知创建单独的类。我们的SimpleTradeAuditor类会在事件发生时继续接收这些通知。如果我们想根据事件的类型做单独的处理,我们可以简单的添加一个检查事件的类型。最后,如果需要,我们也可以定义一个类有多个订阅方法:

        public class AllTradesAuditor {
            private List<BuyEvent> buyEvents = Lists.newArrayList();
            private List<SellEvent> sellEvents = Lists.newArrayList();
            public AllTradesAuditor(EventBus eventBus) {
                eventBus.register(this);
            }
            @Subscribe
            public void auditSell(SellEvent sellEvent) {
                sellEvents.add(sellEvent);
                System.out.println("Received TradeSellEvent " + sellEvent);
            }
            @Subscribe
            public void auditBuy(BuyEvent buyEvent) {
                buyEvents.add(buyEvent);
                System.out.println("Received TradeBuyEvent " + buyEvent);
            }
        }

    上面我们创建了一个包含两个事件处理方法的类,AllTradesAuditor方法将接收所有交易事件的通知,它只是一个被EventBus(基于事件类型)调用的方法。采取一个极端,我们可以创建一个事件处理方法,该方法接受一个Object类型的对象,Object在java中是所有对象的父类,这样我们就可以接收任何和所有由EventBus处理的事件的通知了。最后,没有什么能够阻止我们拥有多个EventBus实例。如果我们要重构BuySellTradeExecutor类成两个独立的类,我们可以为每个类注入一个单独的EventBus实例。那么它将是一个注入正确EventBus实例审计类的方法,我们就有了一套完整独立的发布-订阅事件。

    取消订阅

    正如我们想订阅事件,有些情况下我们可能需要取消事件的订阅。可以通过订阅对象的eventbus.unregister方法实现。例如,如果我们需要取消订阅事件,我们可以将下面的方法添加到我们的订阅类:

        public void unregister(){
            this.eventBus.unregister(this);
        }

    一旦调用此方法,该特定实例将停止接收无论多久以前注册的事件。其他注册了相同事件的实例则会继续接收通知。

    异步EventBus

    Eventbus处理所有的事件都以串行的方式,这种事件处理方法确保了处理的轻量性。不过,我们仍然有另外的选择AsyncEventBus,AsyncEventBus类提供了与EcentBus相同的功能,但是使用了java.util.concurrent.executor实例来进行方法的异步处理。

    我们可以通过类似于EventBus实例的方式,创建一个AsyncEventBus实例:

    AsyncEventBus asyncEventBus = new AsyncEventBus(executorService);

    上面我们通过一个ExecutorService实例创建了AsyncEventBus实例,除了ExecutorService实例,也可以通过提供一个字符串标识符创建AsyncEventBus。当我们的订阅者在接收事件时需要执行繁重的处理时,使用AsyncEventBus会很有用。

    DeadEvents

    当EventBus收到事件通过post方法发送的通知,并且没有注册的订阅者,那么事件则是被DeadEvent类的一个实例包裹。当试图确保所有的事件都有注册的订阅者时,有一个DeadEvents实例的订阅类是非常有用的。DeadEvents类提供了一个公共的getEvent方法,可以用来检查那些未交付的原始事件。例如,我们可以通过下面的方式创建一个非常简单的例子:

    public class DeadEventSubscriber {
        public DeadEventSubscriber(EventBus eventBus) {
            eventBus.register(this);
        }
        @Subscribe
        public void handleUnsubscribedEvent(DeadEvent deadEvent) {
            System.out.println("No subscribers for " + deadEvent.getEvent());
        }
    }

    上面简单的对任何DeadEvent实例进行了注册,并记录了那些没有订阅者的事件。

    Dependency injection依赖注入

    为了确保我们为相同的EventBus实例注册了订阅者和发布者,使用依赖注入框架(Spring或Guice)显得很有意义。接下来的例子中,我们会介绍怎么配置Spring框架在SimpleTradeAuditor和SimpleTradeExecutor类。首先,我们对SimpleTradeAuditor和SimpleTradeExecutor类做如下的修改:

    @Component
    public class SimpleTradeAuditor {
        private List<TradeAccountEvent> tradeEvents =
                Lists.newArrayList();
        @Autowired
        public SimpleTradeAuditor(EventBus eventBus) {
            eventBus.register(this);
        }
        @Subscribe
        public void auditTrade(TradeAccountEvent tradeAccountEvent) {
            tradeEvents.add(tradeAccountEvent);
            System.out.println("Received trade " + tradeAccountEvent);
        }
    }
    @Component
    public class SimpleTradeExecutor {
        private EventBus eventBus;
        @Autowired
        public SimpleTradeExecutor(EventBus eventBus) {
            this.eventBus = eventBus;
        }
        public void executeTrade(TradeAccount tradeAccount, double
                amount, TradeType tradeType) {
            TradeAccountEvent tradeAccountEvent =
                    processTrade(tradeAccount, amount, tradeType);
            eventBus.post(tradeAccountEvent);
        }
        private TradeAccountEvent processTrade(TradeAccount
                                                       tradeAccount, double amount, TradeType tradeType) {
            Date executionTime = new Date();
            String message = String.format("Processed trade for %s of amount %n type %s @%s", tradeAccount, amount, tradeType, executionTime);
            TradeAccountEvent tradeAccountEvent = new TradeAccountEvent(tradeAccount, amount, executionTime, tradeType);
            System.out.println(message);
            return tradeAccountEvent;
        }
    }

    上面我们简单的为两个类添加了类级别的@Component注解,这是为了使Spring将这些我们想注入的类作为bean。这样,我们就需要使用构造注入,所以在两个类的构造方法上添加了@Autowired注解,@Autowired告诉Spring给两个类注入EventBus的一个实例。最后,我们有我们的配置类,来指示Spring框架在哪里寻找组件,并连接配置类中定义的bean:

    @Configuration
    @ComponentScan(basePackages = {"guava"})
    public class EventBusConfig {
        @Bean
        public EventBus eventBus() {
            return new EventBus();
        }
    }

    上面我们使用了@Configuration注解,它标识了此类作为Spring上下文包含bean的创建和注入。我们定义了eventBus方法构造并且返回了EventBus类的一个实例,它将被注入给其他对象。这种情况下,当我们在SimpleTradeAuditor和SimpleTradeExecutor类的构造方法上使用@Autowire注解,Spring会自动注入相同的EventBus实例,这正是我们所需要的。值得注意的是,Spring默认情况下创建单例类,这也是我们这里想要的。正如我们所看到的,使用依赖注入框架可以确保我们基于事件的系统配置的合理正确。

    Summary

    在本篇中,我们已经介绍了如何通过Guava EventBus类使用基于事件的编程,来减少我们的代码耦合。我们介绍了如何创建一个EventBus实例并注册订阅者和发布者。我们也探讨了强大的使用类型注册那些我们感兴趣的事件。我们了解了AsyncEventBus类,它允许我们发送异步事件。我们看到了如何使用DeadEvent类,以确保我们的事件都拥有订阅者。最后,我们看到了如何使用依赖注入框架来解耦我们基于事件的系统配置。

    下一个系列中, 我们将会学习如何通过Guava对文件进行操作。敬请关注。

© 著作权归作者所有

共有 人打赏支持
Realfighter

Realfighter

粉丝 130
博文 139
码字总数 144659
作品 2
洛阳
程序员
加载中

评论(1)

JsonAndXml
JsonAndXml
Abc
【死磕Sharding-jdbc】—–路由&执行

原文作者:阿飞Javaer 原文链接:https://www.jianshu.com/p/09efada2d086 继续以模块中的为基础,剖析分库分表简单查询SQL实现--,即如何执行简单的查询SQL,接下来的分析以执行SQL语句为例...

飞哥-Javaer ⋅ 05/03 ⋅ 0

Android开源框架源码鉴赏:EventBus

作者:郭孝星 校对:郭孝星 关于项目 Android Open Framework analysis项目主要用来分析Android平台主流开源框架的源码与原理实现。 文章目录 一 注册订阅者 二 发布事件Event 三 接收事件E...

郭孝星 ⋅ 03/27 ⋅ 0

RxJava实现的EventBus-RxBus

使用RxJava2实现RxBus 当我们使用RxJava2实现EventBus模式时,我们称其为RxBus. 注意:这仅作为一个例子。 那么,就让我们来看看如何使用RxJava2实现RxBus吧 首先,你得了解RxJava中有关sub...

塞上牧羊空许约 ⋅ 05/12 ⋅ 0

gis-luq/RuntimeViewer

RuntimeViewer —— 基于ArcGIS Runtime 100.x开发的移动应用程序框架 组件式开发框架,兼容手机、平板多种设备。 平板模式 手机模式 使用说明 项目采用插件化结构设计,文件夹架构说明如下 ...

gis-luq ⋅ 05/11 ⋅ 0

Android之撸一个类似EventBus的轮子

用过eventBus的人都知道,在组件及时通讯中,使用非常方便。 一般我们是这样做的:总结为三部曲: 这就是EventBus的强大之处,用过的人都说好,哈哈哈哈!!!! 上图描述吧。有图有真相 Cr...

还有de小星 ⋅ 05/25 ⋅ 0

gis-luq/RuntimeViewer3D

RuntimeViewer —— 基于ArcGIS Runtime 100.x开发的移动应用程序框架 组件式开发框架,兼容手机、平板多种设备。 平板模式 手机模式 如何快速运行应用程序? 使用git工具下来工程并将工程导...

gis-luq ⋅ 06/20 ⋅ 0

你知道event库吗?教你如何写一个自己的event库

在使用BmobSDK开发App的时候,会有很多异步回调,类似如登录成功,插入数据成功的事件,虽然V3.5.0开始内部用rxjava去重构,也提供了rx风格的api,不过写事件回调的方法还是比较烦,这时候你...

小小琪bmob后端云 ⋅ 03/16 ⋅ 0

Google的Guava类库简介(转)

说明:信息虽然有点旧,至少可以先了解个大概。 Guava是一个Google的基于Java的类库集合的扩展项目,包括collections, caching, primitives support, concurrency libraries, common annotat...

easonjim ⋅ 2017/11/01 ⋅ 0

阿里ARouter路由实现Android模块化开发

概述 从 2016 年开始,模块化在 Android 社区越来越多的被提及。随着移动平台的不断发展,移动平台上的软件慢慢走向复杂化,体积也变得臃肿庞大,为了降低大型软件复杂性和耦合度,同时也为了...

code_xzh ⋅ 04/24 ⋅ 0

Android 计步器应用 - healthgo

healthgo a android pedometer app (安卓计步器) Support Android 4.1- 7.0 Thanks Project/People desc other Realm a mobile database 使用了这个数据库 EventBus a publish/subscribe eve......

五十风 ⋅ 04/13 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

DevOps 资讯 | PostgreSQL 的时代到来了吗 ?

PostgreSQL是对象-关系型数据库,BSD 许可证。拼读为"post-gress-Q-L"。 作者: Tony Baer 原文: Has the time finally come for PostgreSQL?(有删节) 近30年来 PostgreSQL 无疑是您从未听...

RiboseYim ⋅ 13分钟前 ⋅ 0

Cube、Cuboid 和 Cube Segment

1.Cube (或Data Cube),即数据立方体,是一种常用于数据分析与索引的技术;它可以对原始数据建立多维度索引。通过 Cube 对数据进行分析,可以大大加快数据的查询效率 2.Cuboid 在 Kylin 中特...

无精疯 ⋅ 52分钟前 ⋅ 0

github太慢

1:用浏览器访问 IPAddress.com or http://tool.chinaz.com 使用 IP Lookup 工具获得github.com和github.global.ssl.fastly.net域名的ip地址 2:/etc/hosts文件中添加如下格式(IP最好自己查一...

whoisliang ⋅ 53分钟前 ⋅ 0

非阻塞同步之 CAS

为解决线程安全问题,互斥同步相当于以时间换空间。多线程情况下,只有一个线程可以访问同步代码。这种同步也叫阻塞同步(Blocking Synchronization). 这种同步属于一种悲观并发策略。认为只...

长安一梦 ⋅ 今天 ⋅ 0

云计算的选择悖论如何对待?

人们都希望在工作和生活中有所选择。但心理学家的调查研究表明,在多种选项中进行选择并不一定会使人们更快乐,甚至不会产生更好的决策。心理学家Barry Schwartz称之为“选择悖论”。云计算为...

linux-tao ⋅ 今天 ⋅ 0

Redis 注册为 Windows 服务

Redis 注册为 Windows 服务 redis 注册为 windows 服务相关命令 注册服务 redis-server.exe –service-install redis.windows.conf 删除服务 redis-server –service-uninstall 启动服务 re......

Os_yxguang ⋅ 今天 ⋅ 0

世界那么大,语言那么多,为什么选择Micropython,它的优势在哪?

最近国内MicroPython风靡程序界,是什么原因导致它这么火呢?是因为他功能强大,遵循Mit协议开源么? 错!因为使用它真的是太舒服了!!! Micropython的由来,这得益于Damien George这位伟大...

bodasisiter ⋅ 今天 ⋅ 0

docker 清理总结

杀死所有正在运行的容器 docker kill $(docker ps -a -q) 删除所有已经停止的容器(docker rm没有加-f参数,运行中的容器不会删掉) docker rm $(docker ps -a -q) 删除所有未打 dangling 标...

vvx1024 ⋅ 今天 ⋅ 0

关于学习

以前学车的时候,教练说了这样的一句话:如果一个人坐在车上一直学,一直学,反而不如大家轮流着学。因为一个人一直学,就没有给自己留空间来反思和改进。而轮流着学的时候大家下来之后思考上...

mskk ⋅ 今天 ⋅ 0

压缩工具之gzip-bzip2-xz

win下常见压缩工具:rar zip 7z linux下常见压缩工具:zip gz bz2 xz tar.gz tar.bz2 tar.xz gzip 不支持目录压缩 gzip 1.txt #压缩。执行后1.txt消失,生成1.txt.gz压缩文件 gzip -d 1.txt....

ZHENG-JY ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部