基于RxJava实现事件总线

本文介绍RxJava中的Subject,同时对事件总线进行抽象,并给出两种实现扩展,方便使用。

RxJava实现事件总线的方法

Subject: RxJava的事件总线

RxJava实现事件总线很简单,就是创建一个Subject对象即可。

Subject<Object, Object> bus = PublishSubject.create()

Subject既是被观察者Observable,又是观察者(或叫订阅者,Subscriber)。

PublishSubject不是线程安全的,但它提供toSerialized方法可以转化为线程安全的SerializedSubject,事件总线的线程安全版本,如下所示:

Subject<Object, Object> bus = PublishSubject.create().toSerialized()

事件发送

当我们要发送事件(可以是任意对象)时,只需调用onNext方法:

bus.onNext(evt)

其中,evt是任意对象,作为事件发送出去(具体说,应该是“广播”),任何总线的监听者都能够收到这个事件。

事件监听

当我们要监听事件时,只需订阅总线:

bus.subscribe(evt -> {
  // 当监听到事件时,相应的处理代码,(略)
});

其中evt为接受到的事件对象,即发送端发送出去的对象。

关闭事件监听

subscribe方法返回一个Subscription对象,即:

Subscription subscription = bus.subscribe(evt -> ...);

当我们不需要观察事件时,可以取消订阅:

if (!subscription.isUnsubscribed()) {
  subscription.unsubscribe();
}

事件过滤

RxJava有大量强大的操作符,可以方便我们做很多处理, 比如过滤filter,转化map,线程调度observedOn。

实际上,很多情况下我们的监听者不需要处理任何事件,而只关注某些事件,因此需要对监听到的事件做过滤操作,这是RxJava擅长的,在subscribe前用filter操作符,即可:

bus.filter(evt -> ...)
.subscribe(evt -> ...)

事件转化

当我们只对事件中某些属性感兴趣,只需用map操作符将事件转化为需要的属性:

bus.filter(evt -> ...)
.map(evt -> evt.getXXX())
.subscribe(xxx -> ...)

线程调度

上面的事件发送到接受处理的过程,都是在单线程里进行的。我们可以使用observeOn操作符将接收处理逻辑指定到相应的线程池中处理:

bus.filter(evt -> ...)
.map(evt -> evt.getXXX())
.observeOn(Schedulers.computation())   // 使用计算线程池处理
.subscribe(xxx -> ...)

抽象与扩展

事件的定义

事件应该包含以下信息:事件发生的时间,事件源,具体事件。
对于具体事件,需要实现Event接口:

/**
 * @author wen-zhe.liu@asml.com
 *
 */
public interface Event {

}

下面是总线中事件的定义:

/**
 * The Class BusEvent.
 *
 * @author wen-zhe.liu@asml.com
 * @param <E>
 *          the element type
 */
public class BusEvent<E extends Event> {

  private final long occurTime;
  private final Object eventSource;
  private final E event;

  /**
   * Instantiates a new bus event.
   *
   * @param eventSource
   *          the event source
   * @param event
   *          the event
   */
  public BusEvent(Object eventSource, E event) {
    occurTime = System.currentTimeMillis();
    this.eventSource = eventSource;
    this.event = event;
  }

  /**
   * Gets the occur time.
   *
   * @return the occur time
   */
  public long getOccurTime() {
    return occurTime;
  }

  /**
   * usually user don't have to get the content of event source, just to filter its reference.
   *
   * @return the event source
   */
  public Object getEventSource() {
    return eventSource;
  }

  /**
   * Gets the event.
   *
   * @return the event
   */
  public E getEvent() {
    return event;
  }
}

事件总线的抽象

/**
 * The Interface EventBus.
 *
 * @author wen-zhe.liu@asml.com
 */
public interface EventBus {

  /**
   * Post.
   *
   * @param o
   *          the o
   */
  void post(Object o);

  /**
   * Async post.
   *
   * @param o
   *          the o
   */
  void asyncPost(Object o);

  /**
   * Observe.
   *
   * @param <T>
   *          the generic type
   * @param eventType
   *          the event type
   * @return the observable
   */
  <T> Observable<T> observe(Class<T> eventType);

  /**
   * Observe event.
   *
   * @param <T>
   *          the generic type
   * @param eventType
   *          the event type
   * @return the bus event filter
   */
  <T extends Event> BusEventFilter<T> observeEvent(Class<T> eventType);

  /**
   * Observe event.
   *
   * @param <T>
   *          the generic type
   * @param event
   *          the event
   * @return the bus event filter
   */
  <T extends Event> BusEventFilter<T> observeEvent(T event);

  /**
   * Removes the event bus.
   *
   * @param eventType
   *          the event type
   */
  void removeEventBus(Class<?> eventType);

  /**
   * Release
   */
  void release();
}

其中,为了方便使用,定义了内部抽象类BusEventFilter。

 /**
   * The Class BusEventFilter.
   *
   * @param <T>
   *          the generic type
   */
  public abstract class BusEventFilter<T extends Event> {

    protected final Class<T> eventType;
    private final T matchedEvent;  // null for don't have to matched, type is enough
    @SuppressWarnings("rawtypes")
    private final List<Func1<? super BusEvent, Boolean>> filters = new ArrayList<>();

    protected BusEventFilter(Class<T> eventType) {
      this.eventType = eventType;
      this.matchedEvent = null;
    }

    @SuppressWarnings("unchecked")
    protected BusEventFilter(T event) {
      this.eventType = (Class<T>) event.getClass();
      this.matchedEvent = event;
    }

    /** filter bus event.
     *
     * @param filter
     *          the filter
     * @return the bus event filter
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public BusEventFilter<T> filter(Func1<? super BusEvent<T>, Boolean> filter) {
      filters.add((Func1<? super BusEvent, Boolean>) filter);
      return this;
    }

    /** 
     * filter by event source
     * <p>
     * supplier to get event source, to make it calculate in run time, not in first time.
     *
     * @param eventSourceSupplier
     *          the event source supplier
     * @return the bus event filter
     */
    public BusEventFilter<T> filterEventSource(Supplier<Object> eventSourceSupplier) {
      return filter(evt -> evt.getEventSource() == eventSourceSupplier.get());
    }

    /** 
     * filter by event source in a collection
     * <p>
     * supplier to get event source list, to make it calculate in run time, not in first time.
     *
     * @param eventSourceCollectionSupplier
     *          the event source collection supplier
     * @return the bus event filter
     */
    public BusEventFilter<T> filterEventSourceIn(Supplier<Collection<?>> eventSourceCollectionSupplier) {
      return filter(evt -> eventSourceCollectionSupplier.get().stream().anyMatch(
          obj -> obj == evt.getEventSource()));
    }

    /** Then, convert to observable event stream.
     *
     * @return the observable
     */
    public Observable<T> then() {
      return andThen()
      .map(evt -> evt.getEvent())
      .cast(eventType)
      // if don't have to match event, match event type is enough (the case matchedEvent == null)
      .filter(evt -> matchedEvent == null || matchedEvent.equals(evt));
    }

    /** And then.
     *
     * @return the observable
     */
    public Observable<BusEvent> andThen() {
      return filters.stream().reduce(
          observeBusEvent().filter(busEvent -> eventType.isInstance(busEvent.getEvent())), 
          (busEvent, filter) -> busEvent.filter(filter), 
          (a, b) -> a);
    }

    protected abstract Observable<BusEvent> observeBusEvent();
  }

单一总线实现

单一总线是在整个程序范围内,任何事件都发送给这个总线,任何总线的监听器都能够响应事件,这样设计简单、使用方便。其实现简单,就是对RxJava的进行简单的封装而已。

/**
 * The Class RxEventBus. Single event bus for all event.
 *
 * @author wen-zhe.liu@asml.com
 */
public class RxEventBus implements EventBus {

  private final Subject<Object, Object> bus = PublishSubject.create().toSerialized();

  /* (non-Javadoc)
   * @see com.asml.jex.rx.event.EventBus#post(java.lang.Object)
   */
  @Override
  public void post(Object o) {
      bus.onNext(o);
  }

  /* (non-Javadoc)
   * @see com.asml.jex.rx.event.EventBus#asyncPost(java.lang.Object)
   */
  @Override
  public void asyncPost(Object o) {
    Schedulers.computation().createWorker().schedule(() -> 
      bus.onNext(o)
    );
  }

  @Override
  public void removeEventBus(Class<?> eventType) {
    // nothing to do as it is a single event bus
  }

  @Override
  public void release() {
    bus.onCompleted();
  }

  /* (non-Javadoc)
   * @see com.asml.jex.rx.event.EventBus#observe(java.lang.Class)
   */
  @Override
  public <T> Observable<T> observe(Class<T> eventType) {
    return bus.ofType(eventType);
  }

  /* (non-Javadoc)
   * @see com.asml.jex.rx.event.EventBus#observeEvent(java.lang.Class)
   */
  @Override
  public <T extends Event> BusEventFilter<T> observeEvent(Class<T> eventType) {
    return new BusEventFilter<>(eventType);
  }

  /* (non-Javadoc)
   * @see com.asml.jex.rx.event.EventBus#observeEvent(T)
   */
  @Override
  public <T extends Event> BusEventFilter<T> observeEvent(T event) {
    return new BusEventFilter<>(event);
  }

  /**
   * The Class BusEventFilter.
   *
   * @param <T>
   *          the generic type
   */
  public class BusEventFilter<T extends Event> extends EventBus.BusEventFilter<T> {

    private BusEventFilter(Class<T> eventType) {
      super(eventType);
    }

    @SuppressWarnings("unchecked")
    private BusEventFilter(T event) {
      super(event);
    }

    @Override
    protected Observable<BusEvent> observeBusEvent() {
      return observe(BusEvent.class);
    }
  }
}

多总线实现

如果有太多的监听器(比如10000个),任何一个事件发送给单一总线,所有监听器都会响应,尽管绝大多数都在过滤阶段忽略掉,但仍然会带来一定的系统开销。更好的办法就是采用多总线的方式,通过多个总线来分担事件,事件发往响应的总线,而只有少数几个监听器接收到事件,从而提高效率。

关于多总线如何分工,有多种办法。这里根据不同事件类型,选择不同的事件总线。一种事件类型对应一个事件总线。

/**
 * The Class RxEventBus. Multiple event buses, each one to one event type.
 *
 * @author wen-zhe.liu@asml.com
 */
public class RxEventBuses implements EventBus {

  private final Map<Class<?>, Subject<Object, Object>> eventBuses = new IdentityHashMap<>();

  /**
   * Post event, support BusEvent, support hierarchy event.
   *
   * @param o
   *          the o
   */
  @Override
  public synchronized void post(Object o) {
    Class<? extends Object> eventType = checkBusEvent(o).getClass();

    List<Subject<Object, Object>> evtBuses = eventBuses.entrySet().stream()
    .filter(eventBus -> eventBus.getKey().isAssignableFrom(eventType))
    .map(eventBus -> eventBus.getValue())
    .collect(Collectors.toList());  // to list: prevent ConcurrentModificationException in forEach as stream

    for (Subject<Object, Object> eventBus : evtBuses) {
      eventBus.onNext(o);
    }
  }

  private Object checkBusEvent(Object o) {
    return o instanceof BusEvent<?> ? ((BusEvent<?>) o).getEvent() : o;
  }

  private synchronized Subject<Object, Object> getOrCreateBus(Class<?> eventType) {
    Subject<Object, Object> bus = eventBuses.get(eventType);
    if (bus == null) {
      bus = PublishSubject.create().toSerialized();
      eventBuses.put(eventType, bus);
    }
    return bus;
  }

  /* (non-Javadoc)
   * @see com.asml.jex.rx.event.EventBus#removeEventBus(java.lang.Class)
   */
  @Override
  public void removeEventBus(Class<?> eventType) {
    Subject<Object, Object> bus = null;
    synchronized (this) {
      bus = eventBuses.remove(eventType);
    }
    if (bus != null) {
      bus.onCompleted();
    }
  }

  @Override
  public synchronized void release() {
    eventBuses.values().forEach(bus -> bus.onCompleted());
    eventBuses.clear();
  }

  /**
   * Async post.
   *
   * @param o
   *          the o
   */
  @Override
  public void asyncPost(Object o) {
    Schedulers.computation().createWorker().schedule(() -> 
      post(o)
    );
  }

  /**
   * Observe.
   *
   * @param <T>
   *          the generic type
   * @param eventType
   *          the event type
   * @return the observable
   */
  @Override
  public <T> Observable<T> observe(Class<T> eventType) {
    Subject<Object, Object> bus = getOrCreateBus(eventType);
    return bus.ofType(eventType);
  }

  /**
   * Observe event.
   *
   * @param <T>
   *          the generic type
   * @param eventType
   *          the event type
   * @return the bus event filter
   */
  @Override
  public <T extends Event> BusEventFilter<T> observeEvent(Class<T> eventType) {
    return new BusEventFilter<>(eventType);
  }

  /**
   * Observe event.
   *
   * @param <T>
   *          the generic type
   * @param event
   *          the event
   * @return the bus event filter
   */
  @Override
  public <T extends Event> BusEventFilter<T> observeEvent(T event) {
    return new BusEventFilter<>(event);
  }

  /**
   * The Class BusEventFilter.
   *
   * @param <T>
   *          the generic type
   */
  public class BusEventFilter<T extends Event> extends EventBus.BusEventFilter<T> {

    protected BusEventFilter(Class<T> eventType) {
      super(eventType);
    }

    private BusEventFilter(T event) {
      super(event);
    }

    @Override
    protected Observable<BusEvent> observeBusEvent() {
      return getOrCreateBus(eventType).ofType(BusEvent.class);
    }
  }
}

事件总线仓库

为了方便使用,用户代码无需手动创建具体的事件总线,而交给事件总线仓库,从仓库中拿就行了。

/**
 * The Class EventBusRepo.
 *
 * @author wen-zhe.liu@asml.com
 */
public class EventBusRepo {

  public static EventBus eventBus = new RxEventBuses();

  static {
    Runtime.getRuntime().addShutdownHook(new Thread(() -> eventBus.release()));
  }

  /**
   * Gets the.
   *
   * @return the event bus
   */
  public static EventBus get() {
    return eventBus;
  }

  /**
   * Sets the event bus.
   *
   * @param evtBus
   *          the new event bus
   */
  public static void setEventBus(EventBus evtBus) {
    eventBus = evtBus;
  }
}

缺省采用多总线,用户也可以采用setEventBus更改。

使用示例

定义事件

用户可以定义事件,只要实现Event接口即可,可以说普通类,也可以说枚举。举例如下:

public enum XxEvent implements Event {
    XX_LOADED, XX_CHANGED
}
public class XxRemoved implements Event {
   // ... code of XxRemoved
}

根据惯例,事件命名规则为:名称+过去分词,如XX_LOADED, XX_CHANGED, XxRemoved,等。

如果具体事件无状态,可以定义为枚举值比较方便(这也是为什么Event要定义为接口的原因,因为枚举只能实现接口,不能继承类)。

如果事件有状态,只能定义为普通类。由于事件可能在多个线程中传输,建议设计成不可变类。否则,要考虑线程安全问题。

发送事件

可以从事件总线仓库中拿到具体的事件总线,调用post方法,将事件封装BusEvent中,指定事件源,如下所示:

EventBusRepo.get().post(new BusEvent<>(事件源对象, XX_LOADED));

在DDD中,领域实体可以发布领域事件,可以将上面代码封装在领域实体的基类里。

/**
 * @author wen-zhe.liu@asml.com
 *
 */
public abstract class DomainEntity<T> {
  ...
  protected void postEvent(DomainEvent event) {
    EventBusRepo.get().post(new BusEvent<>(this, event));
    ...
  }

在具体的领域实体类中,可以使用postEvent方法简单地发布事件:

postEvent(XX_LOADED);

接收事件

接收事件代码一般如下所示:

EventBusRepo.get().observeEvent(eventType)
.filter(evt -> ...)
.then()
.subscribe(evt -> ...)

通过observeEvent(eventType)观察事件,这里eventType可以说事件的类对象,如 XxRemoved.class, 也可以说是对象(一般为枚举值),如XX_LOADED

然后一般都要对事件进行过滤。通常情况下会对事件源进行过滤,即只对某些事件源感兴趣,除了上面的filter方法之外,我还提供了针对事件源过滤更便捷的方法,如下:

.filterEventSource(() -> 事件源对象)

只有事件源对象匹配(引用相等)才能通过。这里函数参数使用一等函数而不是直接的事件源对象,是为了避免在初始化时设置,以解决事件源对象重新赋值的情况。

有时候我们很难定位到具体的事件源对象,只能定位到事件源的集合,那么可以使用以下过滤方法:

.filterEventSourceIn(() -> 事件源对象的集合)

在DDD中,聚合根对象可以监听其内部实体对象发送到事件,因此为了简化用户代码,在聚合根对象的基类中进行封装:

public abstract class DomainAggregateRoot<T> extends DomainEntity<T> {

  private final transient RxSubscriptions subscriptions = new RxSubscriptions();

  public synchronized void subscribeEvents() {
    if (subscriptions.isEmpty()) {
      subscriptions.addAll(createEventObservers());
    }
  }

  public synchronized void unsubscribeEvents() {
    subscriptions.release();
  }

  protected Collection<Subscription> createEventObservers() {
    return Collections.emptyList();
  }

  protected static <T extends DomainEvent> BusEventFilter<T> observeEvent(Class<T> eventType) {
    return EventBusRepo.get().observeEvent(eventType);
  }

  protected static <T extends DomainEvent> BusEventFilter<T> observeEvent(T event) {
    return EventBusRepo.get().observeEvent(event);
  }

  public DomainAggregateRoot<T> observe(Subscription... ss) {
    subscriptions.observe(ss);
    return this;
  }
}

具体的聚合根类可以覆盖createEventObservers方法,从而实现订阅生命周期管理。

—— 本博客所有内容均为原创,转载请注明作者和出处 ——–
作者:刘文哲
联系方式:liuwenzhe2008@qq.com
博客:http://blog.csdn.net/liuwenzhe2008

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值