guava, https://github.com/google/guava 是一个很是有名的Java类库,提供了不少在平常开发中经常使用的集合、函数接口等。此外,guava还提供了一个模块叫作event bus,生产者往event bus上投递消息,event bus负责回调订阅了此类消息的回调函数,实现了消息生产者和消费者之间的解耦和异步处理。如下是一个简单的例子:git
public class SimpleListener { @Subscribe public void task(String s) { System.out.println("do task(" + s + ")"); } }
public class SimpleEventBusExample { public static void main(String[] args) { EventBus eventBus = new EventBus(); eventBus.register(new SimpleListener()); System.out.println("Post Simple EventBus Example"); eventBus.post("Simple EventBus Example"); } }
outputgithub
Post Simple EventBus Example do task(Simple EventBus Example)
在以前的例子和guava的官方文档里面能够看到,guava的event bus使用方式以下spring
1. 声明一个event bus对象(线程安全,因此能够作到全局惟一,并且订阅者和发布者必须共享这个event bus对象)安全
2. 对于订阅者,支持 @Subscribe,定义处理消息的回调函数。并发
3. 对每个订阅者,须要调用event bus的register方法,才能收到消息订阅。app
对于1 和 2,都比较好。可是对于第3步来讲,则有一点困难。由于框架
1. 在spring中,一般的你的订阅者还会依赖其余的spring管理的bean,因而你的订阅者也会被归入到spring的生命周期的管理中来,这样若是用new的方式来初始化一个订阅者,显得很是的"不spring"。异步
2. 对于每一个订阅者,都要显式的注册到event bus里面,这样并无作到关注点分离。理想的状况下,订阅者是不该该去关注如何注册到event bus中。它只应该申明处理消息的回调函数,以及该回调函数是否可以并发调用。注册到event bus中这件事,对于订阅者应该是被动且自动的(只须要申明本身是否想注册到event bus,而不须要关心细节)。这一点在多人开发,而且项目人员水平良莠不齐的时候,尤为重要。ide
那么如何作到自动注册呢?其实答案很简单,在spring中,ApplicationContext这个类提供了一系列的方法去获取到当前spring context中的bean,只须要在event bus初始化以后,经过ApplicationContext来获取当前有哪些订阅者,而且主动的去注册就行。因为,guava的实现中,并无要求订阅者实现某个接口,而是用注解的方式来声明回调函数的,则这篇文章中的实现,也不须要订阅者去实现某个接口,而是用注解的方式来申明本身是一个订阅者。代码以下函数
先声明一个注解
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Service public @interface EventSubscriber {}
对于一个订阅者,在类的接口上,加上这个注解,而且肯定这个bean会在归入spring的生命周期管理中。
@EventSubscriber public class SimpleSubscriber implements HiDasSubscriber {
@Autowired Somebean somebean; @Subscribe @AllowConcurrentEvents public Integer logEventToDbAndUpdateStatus(String event) { System.out.println("receive a event:"+event); } }
声明一个event bus的服务,而且在初始化以后,经过ApplicationContext的 getBeansWithAnnotation 的方法把全部的订阅者获取,而且注册到event bus中。
@Service public class EventBusService implements InitializingBean{ private EventBus innerBus; @Inject private ApplicationContext appContext; public void unRegister(Object eventListener){ innerBus.unregister(eventListener); } public void postEvent(String event){ innerBus.post(event); } public void register(Object eventListener){ innerBus.register(eventListener); } @Override public void afterPropertiesSet() throws Exception { innerBus = new AsyncEventBus("Hidas-event-bus", Executors.newCachedThreadPool()); appContext.getBeansWithAnnotation(EventSubscriber.class).forEach((name, bean) -> { innerBus.register(bean); }); } }
完成了这两步以后。若是其余的消息生产者要往event bus上发消息,只须要注入这个event bus service,而且调用其post方法就行了。
注:这个只是例子,在实际项目中,对全部的消息都会声明一个基类或者接口,每一个订阅者对只会处理消息的某个具体实现。这样event bus会根据消息的具体类型,来调用真正关注此类消息的订阅者的回调函数。这样比起让全部消息订阅者去实现一个 onEvent(BaseEvent event)的方法, 其实是避免了一个多路分配的问题。
对于一些类库在spring中使用,这种方法其实是一种通用的模式,实现某个接口或者编写某个注解,而后经过ApplicationContext来获取对应的bean,以后进行某些注册或者组装操做。这样的话,可让业务的代码,和框架的代码作到必定程度的关注点分离。