grails-platform-core插件的事件总线（Events Bus）?
李小翔
2014-07-04 19:23

内容列表
事件触发（Sending Events） 	 # 
事件监听（Listening Events） 	 # 
监听 GROM 事件（Listening GORM events） 	 # 

BroToolkit 中集成了插件 platform-core，BroFramework、BroBPM中都使用其事件总线（插件的其他功能都被禁用了）作为默认的事件总线（如流程提醒服务）；此外，还通过  CometD 插件实现了类似的异步消息总线、尤其是浏览器和服务器之间的消息。
这里简单介绍一下常用API的基本使用，更多信息参考 Events Bus（英文）。

事件触发（Sending Events）
在 Domain、Controller、Service 类中，可以直接调用 event 方法发起一个事件：
event ( topic, [data, params, callbackClosure] )
event ( Map args, [callbackClosure] )
主要参数包括：
topic: 主题，监听器通过 topic 和 namespace 来监控频道
data: 传递给监听器的数据
params：一些参数，Map格式，常用的有 fork（是否异步，默认为true）、namespace/for（主题所在的命名空间，默认为 app）、onError、onReply
args：包含上面所有的Map
callbackClosure：事件执行完后的回调闭包
个人更喜欢第二种调用方式，示例如下：
// 异步事件（多线程）
event( for: bropen.bpm.Constants.PLUGIN_NAME, topic: ”taskAsync”, data: [foo:1, bar: 2], fork: true )
// 同步事件（单线程，如果监听器中有异常，则会一直抛到当前线程，并导致后面的代码不再执行）
event( for: bropen.bpm.Constants.PLUGIN_NAME, topic: ”taskSync”, data: [foo:1, bar: 2], fork: false )
该方法返回一个 EventReply 对象（实现了Future接口），提供了下面的一些方法：
List<Object> getValues() : 获得所有监听器的返回值
Object getValue() : getValues() 的第一个
int size() : 触发的监听器数量
List<Throwable> getErrors() : 错误列表
boolean hasErrors() : 是否有异常
EventReply waitFor() : 等待所有监听器执行完毕
EventReply waitFor(long time) : 等待所有监听器执行完毕、或者一定时间
需要注意的是，上面的调用任何方法，都会导致当前线程等待，除了异常处理方式不一样外，效果和同步事件类似。
此外，可以使用通配符后缀在多个频道触发事件，如：
// 所有以”role-”开头的命名空间的、以”chat-“开头的主题
event for:’role-*’, topic:’chat-*’, data:session.user
// app命名空间的所有主题
event ’*’

事件监听（Listening Events）
有几种事件监听的方式。
常用的是在 Service 类中，使用注解 @grails.events.Listener 来注册监听方法，该方法会直接编译到class文件中，因此，需要谨慎使用常量。
示例如下：
class SomeService{ 
   @grails.events.Listener(namespace = bropen.bpm.Constants.PLUGIN_NAME, topic = ’taskAsync’)
   def myMethod(Map data){ 
       do something ...
   } 
} 
这里的监听方法一般只有一个参数，表明要监听频道的数据类型（如果相同频道会发送不同类型的数据，可以使用参数类型进行过滤，或者使用 Object 类型监听所有）；此外，还可以使用 EventMessage 类型的参数来监听所有。
同样，这里的命名空间、主题参数也支持通配符后缀，如：
@grails.events.Listener(namespace = ”role-*”, topic = ’chat-*’)
其次，可以在 Domain、Controller、Service 类中，调用 on 方法来动态注册一个监听器，如：
class User() { 
   def bootStrapInit() { 
       // 监听命名空间 foobar 下的 taskAsync 事件
        on(”foobar”, ”taskAsync”) {  Map data->
           do something...
       } 
       // 监听命名空间 app 下的 taskAsync 事件
        on(”taskAsync”) { 
           do something...
       } 
   } 
} 
每一个监听器会生成一个唯一ID，格式为： [namespace://]topic[:package.Class][#method][@hashcode]。可以通过这个ID来统计、删除监听器，如：
//count every listeners subscribed to ‘mytopic’ inside TestService
countListeners(”mytopic:my.TestService”)
//count every listeners using gorm namespace
countListeners(”gorm://*”)
//remove every listeners in TestService
removeListeners(”*:my.TestService”)

监听 GROM 事件（Listening GORM events）
在Grails中，可以通过多种方式来监听Domain实例的增删改查等事件，比如Domain类中的 afterInsert 等事件、通用的自定义事件（Custom Event Listeners）、通用的Hibernate事件（Hibernate Events），详见官方手册。
这里，还可以通过命名空间 gorm 来监听Domain实例的增删改操作，如：
class SomeService{ 
   @Listener(namespace = ’gorm’)
   void afterInsert(Author author) { 
        println ”after save author -  $author.name”
   } 
   @Listener(topic = ’beforeInsert’, namespace = ’gorm’)
   void beforeInsertBook(Book book) { 
        println ”will insert book - $book.title”
   } 
   //Will catch everything since we don’t filter on the subject by using EventMessage
    @Listener(topic = ’before*’, namespace = ’gorm’)
   void beforeEachGormEvent(EventMessage message) { 
        println ”gorm event $message.event on domain $message.data.class”
   } 
} 
此外，还可以通过 Events Artifact 来实现更细粒度的事件过滤。