EventBus是Guava的事件处理机制,是设计模式中的观察者模式(生产/消费者编程模型)的优雅实现。对于事件监听和发布订阅模式,EventBus是一个非常优雅和简单解决方案,我们不用创建复杂的类和接口层次结构。

  Observer模式是比较常用的设计模式之一,虽然有时候在具体代码里,它不一定叫这个名字,比如改头换面叫个Listener,但模式就是这个模式。手工实现一个Observer也不是多复杂的一件事,只是因为这个设计模式实在太常用了,Java就把它放到了JDK里面:Observable和Observer,从JDK 1.0里,它们就一直在那里。从某种程度上说,它简化了Observer模式的开发,至少我们不用再手工维护自己的Observer列表了。不过,如前所述,JDK里的Observer从1.0就在那里了,直到Java 7,它都没有什么改变,就连通知的参数还是Object类型。要知道,Java 5就已经泛型了。Java 5是一次大规模的语法调整,许多程序库从那开始重新设计了API,使其更简洁易用。当然,那些不做应对的程序库,多半也就过时了。这也就是这里要讨论知识更新的原因所在。今天,对于普通的应用,如果要使用Observer模式该如何做呢?答案是Guava的EventBus。

  EventBus基本用法:

  使用Guava之后, 如果要订阅消息, 就不用再继承指定的接口, 只需要在指定的方法上加上@Subscribe注解即可。代码如下:

  消息封装类:

public class TestEvent {private final int message;public TestEvent(int message) {        this.message = message;System.out.println("event message:"+message);}public int getMessage() {return message;}
}

  消息接受类:

public class EventListener {public int lastMessage = 0;@Subscribepublic void listen(TestEvent event) {lastMessage = event.getMessage();System.out.println("Message:"+lastMessage);}public int getLastMessage() {      return lastMessage;}
}

  测试类及输出结果:

public class TestEventBus {@Testpublic void testReceiveEvent() throws Exception {EventBus eventBus = new EventBus("test");EventListener listener = new EventListener();eventBus.register(listener);eventBus.post(new TestEvent(200));eventBus.post(new TestEvent(300));eventBus.post(new TestEvent(400));System.out.println("LastMessage:"+listener.getLastMessage());;}
}//输出信息
event message:200
Message:200
event message:300
Message:300
event message:400
Message:400
LastMessage:400

  MultiListener的使用:

  只需要在要订阅消息的方法上加上@Subscribe注解即可实现对多个消息的订阅,代码如下:

public class MultipleListener {public Integer lastInteger;  public Long lastLong;  @Subscribe  public void listenInteger(Integer event) {  lastInteger = event; System.out.println("event Integer:"+lastInteger);}  @Subscribe  public void listenLong(Long event) {  lastLong = event; System.out.println("event Long:"+lastLong);}  public Integer getLastInteger() {  return lastInteger;  }  public Long getLastLong() {  return lastLong;  }
}

  测试类:

public class TestMultipleEvents {@Test  public void testMultipleEvents() throws Exception {  EventBus eventBus = new EventBus("test");  MultipleListener multiListener = new MultipleListener();  eventBus.register(multiListener);  eventBus.post(new Integer(100));eventBus.post(new Integer(200));  eventBus.post(new Integer(300));  eventBus.post(new Long(800)); eventBus.post(new Long(800990));  eventBus.post(new Long(800882934));  System.out.println("LastInteger:"+multiListener.getLastInteger());System.out.println("LastLong:"+multiListener.getLastLong());}
}//输出信息
event Integer:100
event Integer:200
event Integer:300
event Long:800
event Long:800990
event Long:800882934
LastInteger:300
LastLong:800882934

  Dead Event:

  如果EventBus发送的消息都不是订阅者关心的称之为Dead Event。实例如下:

public class DeadEventListener {boolean notDelivered = false;  @Subscribe  public void listen(DeadEvent event) {  notDelivered = true;  }  public boolean isNotDelivered() {  return notDelivered;  }
}

  测试类:

public class TestDeadEventListeners {@Test  public void testDeadEventListeners() throws Exception {  EventBus eventBus = new EventBus("test");               DeadEventListener deadEventListener = new DeadEventListener();  eventBus.register(deadEventListener);  eventBus.post(new TestEvent(200));         eventBus.post(new TestEvent(300));        System.out.println("deadEvent:"+deadEventListener.isNotDelivered());}
}//输出信息
event message:200
event message:300
deadEvent:true

  说明:如果没有消息订阅者监听消息, EventBus将发送DeadEvent消息,这时我们可以通过log的方式来记录这种状态。

  Event的继承:

  如果Listener A监听Event A, 而Event A有一个子类Event B, 此时Listener A将同时接收Event A和B消息,实例如下:

  Listener 类:

public class NumberListener {  private Number lastMessage;  @Subscribe  public void listen(Number integer) {  lastMessage = integer; System.out.println("Message:"+lastMessage);}  public Number getLastMessage() {  return lastMessage;  }
}  public class IntegerListener {  private Integer lastMessage;  @Subscribe  public void listen(Integer integer) {  lastMessage = integer; System.out.println("Message:"+lastMessage);}  public Integer getLastMessage() {  return lastMessage;  }
}  

  测试类:

public class TestEventsFromSubclass {@Test  public void testEventsFromSubclass() throws Exception {  EventBus eventBus = new EventBus("test");  IntegerListener integerListener = new IntegerListener();  NumberListener numberListener = new NumberListener();  eventBus.register(integerListener);  eventBus.register(numberListener);  eventBus.post(new Integer(100));  System.out.println("integerListener message:"+integerListener.getLastMessage());System.out.println("numberListener message:"+numberListener.getLastMessage());eventBus.post(new Long(200L));  System.out.println("integerListener message:"+integerListener.getLastMessage());System.out.println("numberListener message:"+numberListener.getLastMessage());        }
}//输出类
Message:100
Message:100
integerListener message:100
numberListener message:100
Message:200
integerListener message:100
numberListener message:200

  说明:在这个方法中,我们看到第一个事件(新的整数(100))是收到两个听众,但第二个(新长(200 l))只能到达NumberListener作为整数一不是创建这种类型的事件。可以使用此功能来创建更通用的监听器监听一个广泛的事件和更详细的具体的特殊的事件。

  一个综合实例:

public class UserThread extends Thread {private Socket connection;private EventBus channel;private BufferedReader in;private PrintWriter out;public UserThread(Socket connection, EventBus channel) {this.connection = connection;this.channel = channel;try {in = new BufferedReader(new InputStreamReader(connection.getInputStream()));out = new PrintWriter(connection.getOutputStream(), true);} catch (IOException e) {e.printStackTrace();System.exit(1);}}@Subscribepublic void recieveMessage(String message) {//接收Publish事件的地方,就是收和发在一个class中,自己和自己玩了if (out != null) {out.println(message);System.out.println("recieveMessage:"+message);}}@Overridepublic void run() {try {String input;while ((input = in.readLine()) != null) {channel.post(input);//Publish事件的地方}} catch (IOException e) {e.printStackTrace();}//reached eofchannel.unregister(this);try {connection.close();} catch (IOException e) {e.printStackTrace();}in = null;out = null;}
}

mport java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;import com.google.common.eventbus.EventBus;public class EventBusChat {public static void main(String[] args) {EventBus channel = new EventBus();ServerSocket socket;try {socket = new ServerSocket(4444);while (true) {Socket connection = socket.accept();UserThread newUser = new UserThread(connection, channel);channel.register(newUser);newUser.start();}} catch (IOException e) {e.printStackTrace();}}
}

  说明:用telnet命令登录:telnet 127.0.0.1 4444 ,如果你连接多个实例你会看到任何消息发送被传送到其他实例。

 http://www.cnblogs.com/peida/p/EventBus.html

EventBusExplained

Colin Decker edited this page on 25 Jul 2015 · 5 revisions

Pages 55

  • Introduction
  • Basic Utilities
    • Using/avoiding null

      • Optional
    • Preconditions
    • Ordering
      • Creation
      • Chaining
      • Application
    • Object methods
      • equals
      • hashCode
      • toString
      • compare/compareTo
    • Throwables
  • Collections
    • Immutable collections
    • New collection types
      • Multiset
      • Multimap
      • BiMap
      • Table
      • ClassToInstanceMap
      • RangeSet
    • Utility Classes
      • Iterables
      • Lists
      • Sets
      • Maps
      • Multisets
      • Multimaps
      • Tables
    • Extension Utilities
      • Forwarding Decorators
      • PeekingIterator
      • AbstractIterator
  • Caches
    • Applicability
    • Population
    • Eviction
      • Removal Listeners
      • Refresh
      • Timed Eviction
      • Size Caps
      • Garbage Collection
      • Explicit Removals
    • Features
      • Statistics
    • Interruption
  • Functional Idioms
    • Obtaining
    • Using Predicates
    • Using Functions
  • Concurrency
    • ListenableFuture
    • Service
      • Using
      • Implementations
  • Strings
    • Joiner
    • Splitter
    • CharMatcher
    • Charsets
  • Networking
    • InternetDomainName
  • Primitives
    • Primitive arrays
    • General utilities
    • Byte conversion
    • Unsigned support
  • Ranges
    • Building
    • Operations
    • Discrete Domains
  • I/O
    • Closing Resources
  • Hashing
    • BloomFilter
  • EventBus
  • Math
    • Integral

      • Overflow Checking
    • Floating Point
  • Reflection
    • TypeToken
    • Invokable
    • Dynamic Proxies
    • ClassPath
  • Releases
    • Release 19
    • Release 18
    • Release 17
    • Release 16
    • Release 15
    • Release 14
    • Release 13
    • Release 12
    • Release 11
    • Release 10
  • Tips
    • Philosophy
    • Building with Guava
    • Shrinking JARs with ProGuard
    • Translating from Apache Commons
    • Guava and Compatibility
    • Idea Graveyard
    • Friends of Guava
    • How to Contribute
  • Glossary
  • Mailing List
  • Stack Overflow
  • Footprint of JDK/Guava data structures
Clone this wiki locally

Clone in Desktop

EventBus allows publish-subscribe-style communication between components without requiring the components to explicitly register with one another (and thus be aware of each other). It is designed exclusively to replace traditional Java in-process event distribution using explicit registration. It is not a general-purpose publish-subscribe system, nor is it intended for interprocess communication.

Example

// Class is typically registered by the container.
class EventBusChangeRecorder {@Subscribe public void recordCustomerChange(ChangeEvent e) {recordChange(e.getChange());}
}
// somewhere during initialization
eventBus.register(new EventBusChangeRecorder());
// much later
public void changeCustomer() {ChangeEvent event = getChangeEvent();eventBus.post(event);
}

One-Minute Guide

Converting an existing EventListener-based system to use the EventBus is easy.

For Listeners

To listen for a specific flavor of event (say, a CustomerChangeEvent)...

  • ...in traditional Java events: implement an interface defined with the event -- such asCustomerChangeEventListener.
  • ...with EventBus: create a method that accepts CustomerChangeEvent as its sole argument, and mark it with the @Subscribe annotation.

To register your listener methods with the event producers...

  • ...in traditional Java events: pass your object to each producer'sregisterCustomerChangeEventListener method. These methods are rarely defined in common interfaces, so in addition to knowing every possible producer, you must also know its type.
  • ...with EventBus: pass your object to the EventBus.register(Object) method on anEventBus. You'll need to make sure that your object shares an EventBus instance with the event producers.

To listen for a common event supertype (such as EventObject or Object)...

  • ...in traditional Java events: not easy.
  • ...with EventBus: events are automatically dispatched to listeners of any supertype, allowing listeners for interface types or "wildcard listeners" for Object.

To listen for and detect events that were dispatched without listeners...

  • ...in traditional Java events: add code to each event-dispatching method (perhaps using AOP).
  • ...with EventBus: subscribe to DeadEvent. The EventBus will notify you of any events that were posted but not delivered. (Handy for debugging.)

For Producers

To keep track of listeners to your events...

  • ...in traditional Java events: write code to manage a list of listeners to your object, including synchronization, or use a utility class like EventListenerList.
  • ...with EventBusEventBus does this for you.

To dispatch an event to listeners...

  • ...in traditional Java events: write a method to dispatch events to each event listener, including error isolation and (if desired) asynchronicity.
  • ...with EventBus: pass the event object to an EventBus's EventBus.post(Object) method.

Glossary

The EventBus system and code use the following terms to discuss event distribution:

Event Any object that may be posted to a bus.
Subscribing The act of registering a listener with an EventBus, so that its handler methodswill receive events.
Listener An object that wishes to receive events, by exposing handler methods.
Handler method A public method that the EventBus should use to deliver posted events. Handler methods are marked by the @Subscribe annotation.
Posting an event Making the event available to any listeners through the EventBus.

FAQ

Why must I create my own Event Bus, rather than using a singleton?

EventBus doesn't specify how you use it; there's nothing stopping your application from having separate EventBus instances for each component, or using separate instances to separate events by context or topic. This also makes it trivial to set up and tear down EventBus objects in your tests.

Of course, if you'd like to have a process-wide EventBus singleton, there's nothing stopping you from doing it that way. Simply have your container (such as Guice) create the EventBus as a singleton at global scope (or stash it in a static field, if you're into that sort of thing).

In short, EventBus is not a singleton because we'd rather not make that decision for you. Use it how you like.

Can I unregister a listener from the Event Bus?

Yes, using EventBus.unregister, but we find this is needed only rarely:

  • Most listeners are registered on startup or lazy initialization, and persist for the life of the application.
  • Scope-specific EventBus instances can handle temporary event distribution (e.g. distributing events among request-scoped objects)
  • For testing, EventBus instances can be easily created and thrown away, removing the need for explicit unregistration.

Why use an annotation to mark handler methods, rather than requiring the listener to implement an interface?

We feel that the Event Bus's @Subscribe annotation conveys your intentions just as explicitly as implementing an interface (or perhaps more so), while leaving you free to place event handler methods wherever you wish and give them intention-revealing names.

Traditional Java Events use a listener interface which typically sports only a handful of methods -- typically one. This has a number of disadvantages:

  • Any one class can only implement a single response to a given event.
  • Listener interface methods may conflict.
  • The method must be named after the event (e.g. handleChangeEvent), rather than its purpose (e.g. recordChangeInJournal).
  • Each event usually has its own interface, without a common parent interface for a family of events (e.g. all UI events).

The difficulties in implementing this cleanly has given rise to a pattern, particularly common in Swing apps, of using tiny anonymous classes to implement event listener interfaces.

Compare these two cases:

   class ChangeRecorder {void setCustomer(Customer cust) {cust.addChangeListener(new ChangeListener() {public void customerChanged(ChangeEvent e) {recordChange(e.getChange());}};}}

versus

   // Class is typically registered by the container.class EventBusChangeRecorder {@Subscribe public void recordCustomerChange(ChangeEvent e) {recordChange(e.getChange());}}

The intent is actually clearer in the second case: there's less noise code, and the event handler has a clear and meaningful name.

What about a generic Handler<T> interface?

Some have proposed a generic Handler<T> interface for EventBus listeners. This runs into issues with Java's use of type erasure, not to mention problems in usability.

Let's say the interface looked something like the following:

interface Handler<T> {void handleEvent(T event);
}

Due to erasure, no single class can implement a generic interface more than once with different type parameters. This is a giant step backwards from traditional Java Events, where even ifactionPerformed and keyPressed aren't very meaningful names, at least you can implement both methods!

Doesn't EventBus destroy static typing and eliminate automated refactoring support?

Some have freaked out about EventBus's register(Object) and post(Object) methods' use of the Object type.

Object is used here for a good reason: the Event Bus library places no restrictions on the types of either your event listeners (as in register(Object)) or the events themselves (in post(Object)).

Event handler methods, on the other hand, must explicitly declare their argument type -- the type of event desired (or one of its supertypes). Thus, searching for references to an event class will instantly find all handler methods for that event, and renaming the type will affect all handler methods within view of your IDE (and any code that creates the event).

It's true that you can rename your @Subscribed event handler methods at will; Event Bus will not stop this or do anything to propagate the rename because, to Event Bus, the names of your handler methods are irrelevant. Test code that calls the methods directly, of course, will be affected by your renaming -- but that's what your refactoring tools are for. We see this as a feature, not a bug: being able to rename your handler methods at will lets you make their meaning clearer.

What happens if I register a listener without any handler methods?

Nothing at all.

The Event Bus was designed to integrate with containers and module systems, with Guice as the prototypical example. In these cases, it's convenient to have the container/factory/environment passevery created object to an EventBus's register(Object) method.

This way, any object created by the container/factory/environment can hook into the system's event model simply by exposing handler methods.

What Event Bus problems can be detected at compile time?

Any problem that can be unambiguously detected by Java's type system. For example, defining a handler method for a nonexistent event type.

What Event Bus problems can be detected immediately at registration?

Immediately upon invoking register(Object) , the listener being registered is checked for the well-formedness of its handler methods. Specifically, any methods marked with @Subscribe must take only a single argument.

Any violations of this rule will cause an IllegalArgumentException to be thrown.

(This check could be moved to compile-time using APT, a solution we're researching.)

What EventBus problems may only be detected later, at runtime?

If a component posts events with no registered listeners, it may indicate an error (typically an indication that you missed a @Subscribe annotation, or that the listening component is not loaded).

(Note that this is not necessarily indicative of a problem. There are many cases where an application will deliberately ignore a posted event, particularly if the event is coming from code you don't control.)

To handle such events, register a handler method for the DeadEvent class. Whenever EventBusreceives an event with no registered handlers, it will turn it into a DeadEvent and pass it your way -- allowing you to log it or otherwise recover.

How do I test event listeners and their handler methods?

Because handler methods on your listener classes are normal methods, you can simply call them from your test code to simulate the EventBus.

Why can't I do with EventBus?

EventBus is designed to deal with a large class of use cases really, really well. We prefer hitting the nail on the head for most use cases to doing decently on all use cases.

Additionally, making EventBus extensible -- and making it useful and productive to extend, while stillallowing ourselves to make additions to the core EventBus API that don't conflict with any of your extensions -- is an extremely difficult problem.

If you really, really need magic thing X, that EventBus can't currently provide, you should file an issue, and then design your own alternative.

https://github.com/google/guava/wiki/EventBusExplained#for-listeners

Guava学习笔记:EventBus(转)相关推荐

  1. Guava学习笔记:简化异常处理的Throwables类

    Guava学习笔记:简化异常处理的Throwables类 参考文章: (1)Guava学习笔记:简化异常处理的Throwables类 (2)https://www.cnblogs.com/peida/ ...

  2. Guava学习笔记(转)

    Guava(瓜娃)学习笔记 Guava工程包含了若干被google的java项目广泛依赖的核心库,例如:集合 [collections] .缓存 [caching] .原生类型支持 [primitiv ...

  3. Guava学习笔记:Google Guava 类库简介

    Guava 是一个 Google 的基于java1.6的类库集合的扩展项目,包括 collections, caching, primitives support, concurrency libra ...

  4. Guava学习笔记:缘起

    缘起 为什么要使用Guava呢?因为在几年前就是用过Guava的部分功能,当时有一个项目需要通过缓存来提升部分功能的性能,对本地应用缓存和分布式应用缓存进行了相关的调研,在调研本地应用缓存的时候,第一 ...

  5. Guava学习笔记:Preconditions优雅的检验参数

    在日常开发中,我们经常会对方法的输入参数做一些数据格式上的验证,以便保证方法能够按照正常流程执行下去.对于可预知的一些数据上的错误,我们一定要做事前检测和判断,来避免程序流程出错,而不是完全通过错误处 ...

  6. Google Guava学习笔记——基础工具类Joiner的使用

    Guava 中有一些基础的工具类,如下所列: 1,Joiner 类:根据给定的分隔符把字符串连接到一起.MapJoiner 执行相同的操作,但是针对 Map 的 key 和 value. 2,Spli ...

  7. Guava学习笔记:Ordering犀利的比较器

    2019独角兽企业重金招聘Python工程师标准>>> Ordering是Guava类库提供的一个犀利强大的比较器工具,Guava的Ordering和JDK Comparator相比 ...

  8. Guava学习笔记(1)--安全地使用null(Using and avoiding null)

    Guava是一个基于java1.6的类库集合, 包含了几个google的核心库, collections, caching, primitives support, concurrency libra ...

  9. Guava学习笔记 6. 字符串处理[Strings]

    非常有用的字符串工具,包括分割.连接.填充等操作 本文简单介绍了一下Guava的一些字符串工具 本文参考自: http://ifeve.com/google-guava-strings/ 并添加了一些 ...

最新文章

  1. 计算机上机报告-origin,Origin 8上机练习1
  2. php 递归太多报错,PHP、递归 - 角落里的星辰的个人空间 - OSCHINA - 中文开源技术交流社区...
  3. SpringMVC入门(二)—— 参数的传递、Controller方法返回值、json数据交互、异常处理、图片上传、拦截器
  4. 使用汇编软件emu8086输出十次Hello World!
  5. 2路由策略_route-map(执行路由策略)
  6. python中 is 和== 的区别
  7. c++ java string_C++中string类型的内部结构是什么?
  8. linux清除asm磁盘分区,ASM Diskgroup添加与删除
  9. 最新数据!妇科类用药领域,中成药占比高
  10. html中加入数据库,HTML中如何连接数据库?
  11. miracl实现sm2
  12. 数字化给财税行业带来的星星之火
  13. java socket 实现原理_java socket原理
  14. [CSS揭秘]菱形图片
  15. Unity 计算屏幕视口UV坐标的几种方法
  16. 病毒HEUR:Trojan-Downloader.Win32.Generic
  17. 搭建Prometheus+Grafana实时监控平台监控Java应用服务
  18. python tkinter 实现简易秒表计时器
  19. 转:企业如何避免“死于开会”
  20. 【工艺】 CNC工艺和开模区别

热门文章

  1. L2-4 哲哲打游戏 (25 分)
  2. 动态规划之图像压缩问题
  3. qr二维码遇到的一些问题
  4. 怎么修剪_杏树怎么育苗?怎么修剪怎么防治病虫害?
  5. cron表达式 每天0点10分和30分_查找特定时间段cron任务方法
  6. .container的应用
  7. c++中wstring 和 string的转换
  8. SQL Server遍历表(临时表)
  9. SQL SERVER 使用订阅发布同步数据库(转)
  10. Greenplum-概念篇