EventBus的初步实践

介绍

EventBus是一类自动事件处理器,也叫事件总线,基于观察者设计模式,使得某些任务能够得到自动处理,例如添加审批日志。

提供了发布-订阅模型,可以方便的在EventBus上注册订阅者,发布者可以简单的将事件传递给EventBus,EventBus会自动将事件传递给相关联的订阅者,只能用于线程间通信。

跟线程池的区别

  • 事件总线可以有多个订阅者,线程池只会有一个执行逻辑
  • 事件总线底层就是基于线程池

事件总线的优点

  • 编程方便
  • 支持同步/异步模式
  • 与aop来比更加灵活

事件总线的缺点

  • 基于内存,如果断电,就会丢失事件
  • 只能单进程使用
  • 代码过于分散,不方便调试

使用方法

// 事件总线的定义
public class EventBusCenter {
 
    private static EventBus eventBus = new EventBus();
 
    private EventBusCenter() {
 
    }
 
    public static EventBus getInstance() {
        return eventBus;
    }
 
    public static void register(Object obj) {
        eventBus.register(obj);
    }
 
    public static void unregister(Object obj) {
        eventBus.unregister(obj);
    }
 
    public static void post(Object obj) {
        eventBus.post(obj);
    }
 
 
}
// Consumer
public class DataObserver {
 
    /**
     * 只有通过@Subscribe注解的方法才会被注册进EventBus
     * 而且方法有且只能有1个参数
     */
    @Subscribe
    public void func(String msg) {
        System.out.println("String msg: " + msg);
    }
}
 
// Provider
public class Test {
 
    public static void main(String[] args) throws InterruptedException {
 
        DataObserver observer = new DataObserver1();
      
        EventBusCenter.register(observer);
 
        // 只有注册的参数类型为String的方法会被调用
        EventBusCenter.post("post string method");
        EventBusCenter.post(123);
    }
}

上述代码分别是事件总线的定义,消费者和提供者,代码逻辑都很简单,但是,实际开发中,会给代码解耦。

实际开发中,类图如下

EventBus的初步实践_第1张图片

/**
 * 事件管理接口,提供消费者订阅、事件投递,作为顶层的接口
 */
public interface IEventBus {
    /**
     * 发布异步事件
     *
     * @param event 事件实体
     */
    void asyncPost(Object event);

    /**
     * 发布同步事件
     *
     * @param event 事件实体
     */
    void syncPost(Object event);

    /**
     * 添加消费者
     *
     * @param obj 消费者对象,默认以class为key
     */
    void addConsumer(Object obj);

    /**
     * 移除消费者
     *
     * @param obj 消费者对象,默认以class为key
     */
    void removeConsumer(Object obj);

    /**
     * 扫描消费者
     *
     * @param packageName 扫描包
     */
    void scanConsumer(String packageName);
}

上面就是顶层的事件接口

/**
 * Guava EventBus和Spring的桥梁
 */
public abstract class AbstractSpringEventBus implements IEventBus, ApplicationContextAware {
    private ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context = applicationContext;
        this.scanConsumer(null);
    }

    @Override
    public void scanConsumer(String packageName) {
        context.getBeansOfType(IEventConsumer.class).forEach((k, v) -> {
            this.addConsumer(v);
        });
    }
}

上面的代码很有趣,首先是抽象类实现接口, 不需要实现所有的接口方法

  1. 实现ApplicationContextAware接口,这是Bean初始化第一步的过程,Bean还没有完全初始化,也就是在Spring启动的时候setApplicationContext会被调用,在其中初始化了应用上下文,并且调用scanConsumer方法扫描消费者的Bean,并注册到事件总线上。
  2. 实现IEventBus接口中的scanConsumer,为第一步提供服务。

当然抽象类还是过于抽象了,实际使用可能还需要分异步和同步事件,况且AbstractSpringEventBus还没有实现addConsumer方法,所以在不同的业务中需要定义不同的实现,如下

/**
 * 业务事件总线
 */
@Component
@Slf4j
public class InvoiceEventBus extends AbstractSpringEventBus {
    private final EventBus asyncEventBus;

    private final EventBus syncEventBus;

    public InvoiceEventBus() {
        //异步事件配置线程池
        asyncEventBus = new AsyncEventBus(new ThreadPoolExecutor(4, Integer.MAX_VALUE,
                60L, TimeUnit.SECONDS,
                new SynchronousQueue<>(),
                new ThreadFactoryBuilder().setNameFormat("invoiceAsyncEventBus-thread-%d").build()),
                new EventBusSubscriberExceptionHandler());

        // 同步事件配置
        syncEventBus = new EventBus("SyncEventBus");
    }

    @Override
    public void asyncPost(Object event) {
        asyncEventBus.post(event);
    }

    @Override
    public void syncPost(Object event) {
        syncEventBus.post(event);
    }

    @Override
    public void addConsumer(Object obj) {
        asyncEventBus.register(obj);
        syncEventBus.register(obj);
    }

    @Override
    public void removeConsumer(Object obj) {
        asyncEventBus.unregister(obj);
        syncEventBus.unregister(obj);
    }

}

至此,上面的代码做好了所有消费者注册的工作,所以后面扩展只需要实现IEventConsumer,就会被注册,就会监听事件

public interface IEventConsumer {
    /**
     * 消费者事件
     * @param event 事件
     */
    void consumer(T event);
}

接口如上所示,实现如下

/**
 * 状态更改事件执行器
 */
@Slf4j
@Component
public class StatusExecutor implements IEventConsumer {

    @Subscribe
    @Override
    public void consumer(StatusChangeEvent statusChangeEvent) {
      log.info("状态变更事件");
            // 业务代码
       }
    }
}

注意到泛型是StatusChangeEvent,也就是说,只要post了这个类型的对象,内部业务代码就会执行.

post逻辑就简单了

@Slf4j
@Service
public class InvoiceRequisitionService {
  @Resource
  private InvoiceEventBus invoiceEventBus;
  
  // 更新service
  // 显然,这里更改了具体的Object,但是可以看到代码的层次关系是很漂亮的。
  public Boolean update(Object object) {
        invoiceEventBus.syncPost(object);
    return true;
  }
}

你可能感兴趣的:(EventBus的初步实践)