原文地址:Guava库学习:学习Guava EventBus(二)EventBus 事件订阅示例html
上一篇Guava库学习:学习Guava EventBus(一)EventBus,咱们简单的对Guava基于事件的编程进行了介绍,学习和了解了EventBus类的使用,本篇起,咱们经过一系列的示例代码深刻的学习EventBus类,本篇学习Guava EventBus(二)EventBus 事件订阅示例。java
订阅Subscribeweb
首先,咱们假定定义了以下所示的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,并简单的输出到控制台。框架
发布Publishingless
首先来看下面的示例代码,代码以下:异步
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实例。async
在上面的例子中,SimpleTradeExecutor类,有一个公共的executeTrade方法,它接收了用来处理交易所须要的全部信息。咱们调用processTrade方法传递所需的信息,当执行交易的时候打印信息到控制台,而后返回一个TradeAccountEvent实例。当processTrade方法执行完成,咱们调用EventBus.post()方法发布返回的TradeAccountEvent实例,并通知全部TradeAccountEvent对象的订阅者。若是咱们快速的比较下SimpleTradeAuditor和SimpleTradeExecutor类,咱们看到,虽然两个类都参与共享所需的信息,可是它们彼此之间没有任何的耦合。post
更细粒度的订阅
上面咱们看到了使用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对文件进行操做。敬请关注。