Java --- Guava Event Bus
Event Bus其实就是发布者-订阅者模式,Guava EventBus提供了一个通用的Event Bus实现,通过注释和约定,将应用中的所有发布者和订阅者模式实现集中管理,而不用针对不同的事件实现不同的事件驱动。
图1 GuavaEvent 结构
简单使用实例
下面是一段使用EventBus的简单示例代码:
publicclass EventBusTest {
publicstaticvoid main(String[] args) { EventBus eb = new EventBus("test");
eb.register(new EventListener());
eb.post(new EventA()); eb.post(new EventB()); }
staticclass EventA{} staticclass EventB{} staticclass EventListener{
@Subscribe publicvoid processEvent(EventA event){ System.out.println("eventA happ"); }
@Subscribe publicvoid processEvent(EventB event){ System.out.println("eventB happ"); } } } |
EventBus使用涉及以下实体:
EventBus: 事件总线,提供订阅者的注册,事件的发布等方法。
Subscriber: 订阅者,可以是任何对象,只要在方法中添加@Subscribe注释,则可以作为一个订阅者。
Event: 事件,订阅者关注的事件,可以是任何对象,在订阅者的@Subscribe方法中的第一个参数为该订阅者订阅的对象类型。
使用流程如下:
1. 获取事件总线
2. 注册订阅者
3. 发布事件
事件的执行
下面是EventBus事件发布和订阅者方法调用的代码段:
publicvoid post(Object event) { Set<Class<?>> dispatchTypes = flattenHierarchy(event.getClass());
booleandispatched = false; for (Class<?> eventType : dispatchTypes) { subscribersByTypeLock.readLock().lock(); try { Set<EventSubscriber> wrappers = subscribersByType.get(eventType);
if (!wrappers.isEmpty()) { dispatched = true; for (EventSubscriber wrapper : wrappers) { enqueueEvent(event, wrapper); } } } finally { subscribersByTypeLock.readLock().unlock(); } }
if (!dispatched && !(eventinstanceof DeadEvent)) { post(new DeadEvent(this, event)); }
dispatchQueuedEvents(); }
void dispatchQueuedEvents() { // don't dispatch if we're already dispatching, that would allow reentrancy // and out-of-order events. Instead, leave the events to be dispatched // after the in-progress dispatch is complete. if (isDispatching.get()) { return; }
isDispatching.set(true); try { Queue<EventWithSubscriber> events = eventsToDispatch.get(); EventWithSubscriber eventWithSubscriber; while ((eventWithSubscriber = events.poll()) != null) { dispatch(eventWithSubscriber.event, eventWithSubscriber.subscriber); } } finally { isDispatching.remove(); eventsToDispatch.remove(); } }
void dispatch(Object event, EventSubscriber wrapper) { try { wrapper.handleEvent(event); } catch (InvocationTargetException e) { try { subscriberExceptionHandler.handleException( e.getCause(), new SubscriberExceptionContext( this, event, wrapper.getSubscriber(), wrapper.getMethod())); } catch (Throwable t) { // If the exception handler throws, log it. There isn't much else to do! Logger.getLogger(EventBus.class.getName()).log(Level.SEVERE, String.format( "Exception %s thrown while handling exception: %s", t, e.getCause()), t); } } } |
从代码中可以看出,EuventBus并没有进行类似异步调用等负载的事件分发策略,而是串行的发布一个事件,然后通知所有感兴趣的订阅者,中间的任何一个订阅者出现阻塞都将造成Event Bus的阻塞。当然用户可以根据需要自己实现自身的异步操作订阅方法,类似于RemovalListeners. asynchronous方法。
@Subscribe publicvoid processEvent(EventA event){
new Thread(new Runnable(){
@Override publicvoid run() { while(true){ System.out.println("eventA happ"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } }
}
}).start();
} |
EventBus的线程安全性
Event Bus类是线程安全的。
EventBus是作为应用中的一个总的事件总线,因此在多线程的使用应该是普遍的。对于EventBus的线程安全性方面使用了一个 RenentrantReadWriteLock来进行同步。
privatefinal ReadWriteLock subscribersByTypeLock = new ReentrantReadWriteLock();
publicvoid register(Object object) { Multimap<Class<?>, EventSubscriber> methodsInListener = finder.findAllSubscribers(object); subscribersByTypeLock.writeLock().lock(); try { subscribersByType.putAll(methodsInListener); } finally { subscribersByTypeLock.writeLock().unlock(); } } |
从register方法和上节中贴出的post方法中都可以看到通过subscribersByTypeLock进行了线程同步。