2019独角兽企业重金招聘Python工程师标准>>>

原文地址: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);}@Subscribepublic 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, doubleamount, TradeType tradeType) {TradeAccountEvent tradeAccountEvent =processTrade(tradeAccount, amount, tradeType);eventBus.post(tradeAccountEvent);}private TradeAccountEvent processTrade(TradeAccounttradeAccount, 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, DatetradExecutionTime) {super(tradeAccount, amount, tradExecutionTime, TradeType.SELL);}}public class BuyEvent extends TradeAccountEvent {public BuyEvent(TradeAccount tradeAccount, double amount, DatetradExecutionTime) {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);}@Subscribepublic 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);}@Subscribepublic 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, doubleamount, TradeType tradeType) {TradeAccountEvent tradeAccountEvent =processTrade(tradeAccount, amount, tradeType);eventBus.post(tradeAccountEvent);}private TradeAccountEvent processTrade(TradeAccounttradeAccount, 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);}@Subscribepublic void auditSell(SellEvent sellEvent) {sellEvents.add(sellEvent);System.out.println("Received TradeSellEvent " + sellEvent);}@Subscribepublic 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);}@Subscribepublic void handleUnsubscribedEvent(DeadEvent deadEvent) {System.out.println("No subscribers for " + deadEvent.getEvent());}}

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

    Dependency injection依赖注入

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

    @Componentpublic class SimpleTradeAuditor {private List<TradeAccountEvent> tradeEvents =Lists.newArrayList();@Autowiredpublic SimpleTradeAuditor(EventBus eventBus) {eventBus.register(this);}@Subscribepublic void auditTrade(TradeAccountEvent tradeAccountEvent) {tradeEvents.add(tradeAccountEvent);System.out.println("Received trade " + tradeAccountEvent);}}
    @Componentpublic class SimpleTradeExecutor {private EventBus eventBus;@Autowiredpublic SimpleTradeExecutor(EventBus eventBus) {this.eventBus = eventBus;}public void executeTrade(TradeAccount tradeAccount, doubleamount, TradeType tradeType) {TradeAccountEvent tradeAccountEvent =processTrade(tradeAccount, amount, tradeType);eventBus.post(tradeAccountEvent);}private TradeAccountEvent processTrade(TradeAccounttradeAccount, 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 {@Beanpublic EventBus eventBus() {return new EventBus();}}

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

    Summary

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

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

转载于:https://my.oschina.net/realfighter/blog/406342

Guava库学习:学习Guava EventBus(二)EventBus 事件订阅示例相关推荐

  1. Guava库学习:学习Collections(二)Lists

    2019独角兽企业重金招聘Python工程师标准>>> 链接地址:http://www.xx566.com/detail/138.html 上一篇学习Collections(一)Fl ...

  2. Guava库学习:学习Concurrency(九)RateLimiter

    2019独角兽企业重金招聘Python工程师标准>>> 链接地址:http://www.xx566.com/detail/164.html 上一篇,Guava库学习:学习Concur ...

  3. 跟着 Guava、Spring 学习如何设计观察者模式

    文章首发在公众号(龙台的技术笔记),之后同步到掘金和个人网站:xiaomage.info 今天讲解一篇行为型设计模式,什么是行为型?行为型主要负责设计 类或对象之间的交互.工作中常用的观察者模式就是一 ...

  4. spring boot guava cache 缓存学习

    http://blog.csdn.net/hy245120020/article/details/78065676 ****************************************** ...

  5. python爬虫学习之路(二)re库的使用方法

    python基础爬虫学习之路(二) 在上一篇文章中,我们已经学习了有关爬虫对URL的访问以及利用xpath表达式对网页中的信息进行提取,这一篇文章我们将从信息提取的角度来进一步学习爬虫. 正则表达式提 ...

  6. [pytorch] PyTorch Metric Learning库代码学习二 Inference

    PyTorch Metric Learning库代码学习二 Inference Install the packages Import the packages Create helper funct ...

  7. Guava RateLimter 基础学习

    Guava RateLimter 基础学习 平滑突发限流 平滑预热限流 原理分析--以平滑突发限流为例 缺点 结合Redis实现分布式 思路 平滑突发限流 public static void mai ...

  8. python内置库之学习ctypes库(二)

    ctypes库踩坑日记2 一.自己实现一个dll文件,再用python的ctypes库调用思路1更清晰 二.生成dll文件 三.ctypes库调用 一.自己实现一个dll文件,再用python的cty ...

  9. EventBus使用详解(二)——EventBus使用进阶

    前言:这段时间感觉自己也有点懒了,真是内心有点自责呢,除了工作,也没做点什么,EventBus也是一周前总结出来的,只能以写博客为名来弥补内心的罪恶感了,集合同事们做的项目,虽然上周开动了,但总感觉大 ...

最新文章

  1. 计算机视觉研究生文献和复现哪个更重要?
  2. Exchange2003-2010迁移系列之十三
  3. sum 去重_Excel函数,用到什么学什么!多条件求和神器之SUMIFS和去重
  4. 我的工作日报 - 2020-9-16 星期三
  5. java输出输入的日期_Java编写的日历,输入年月,输出这个月的日期与星期
  6. 利用计算机的图形能力来进行设计工作的是,计算机一级MSOffice考试巩固练习题...
  7. Media Queries语法总结
  8. shell编程之正则表达式与文本工具
  9. [转]链接中 href='#' 和 href='###' 的区别以及优缺点
  10. 计算H时M分S秒以后是_泵所需轴功率的计算方式
  11. 计算机辅助翻译 教学大纲,《计算机辅助翻译》本科课程教学大纲翻译本科.doc...
  12. Spring的IOC和AOP原理及其使用
  13. 网络上的计算机无权限访问权限,权限,教您怎么解决无internet访问权限
  14. vs2019安装时,一直卡在正在提取文件
  15. Windows10专业版系统镜镜像
  16. 计算机网络基础肖盛文电子书,网络实用教程
  17. 教你免费且快速地搭建个人网站
  18. 安卓修改电池容量教程_安卓手机端修改电池电量图标的教程
  19. CODE CHINA
  20. html颜色对应卡,PANTONE色卡

热门文章

  1. 是同步方法还是 synchronized 代码?-- 详解多线程同步规则
  2. iOS原生定位和反编码
  3. WCF服务创建与使用(双工模式)
  4. ReSIProcate源码目录下功能说明
  5. mac本地搭建kafka
  6. 如何处置你的竞争对手
  7. 好渴望 wacom Intuos3
  8. 最初的感动:各种应用和代码在BCH这里不断复活
  9. 2017-2018-1 20155328 《信息安全系统设计基础》第十四周学习总结
  10. 《linux 内核全然剖析》 mktime.c