Guava EventBus
前言
开发中遇到一个任务的状态变化需要去做不同的事情,随着业务逻辑的丰富各种逻辑判断代码、业务处理代码耦合在任务处理的方法中,随着code review开始重视系统内模块的耦合关系,这种大笼统的代码是不被允许了,首先想到的就是观察者模式解耦。
本文参考自:
https://www.runoob.com/design-pattern/observer-pattern.html
https://www.cnblogs.com/shoren/p/eventBus_springEvent.html
观察者模式
解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
优点:1、观察者和被观察者是抽象耦合的。
2、建立一套触发机制。
缺点:1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
场景:
1、一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
2、一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
3、一个对象必须通知其他对象,而并不知道这些对象是谁。
4、需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
注意:1、JAVA 中已经有了对观察者模式的支持类。
2、避免循环引用。
3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。
调研其它成熟实现的方案
1. Guava EventBus
EventBus 是 Guava 的事件处理机制,是观察者模式(生产/消费模型)的一种实现。基于事件总线的方式开发,可以使我们代码更加简洁,而且解耦,是个轻量级的事件总线,而且事件间可以相互隔离,所以更加灵活多变。如图可知4个关键角色:事件(事件源+事件处理)、事件发布者、事件订阅者、事件总线
实现目标
- 消费者只需要实现接口,即可接收事件消息
- 生产者只需要关心消息的生产
- 事件总线只需要关心生产投递与消息消费的线程、限流等问题
- 使用Spring Boot集成Guava的EventBus,完成EventBus声明和监听类的注册
基础类实现
1 /**
2 * 顶级事件管理接口,提供消费者订阅、事件投递
3 */
4 public interface IEventBus {
5 /**
6 * 发布事件
7 * @param event 事件实体
8 */
9 void post(Object event);
10
11 /**
12 * 添加消费者
13 * @param obj 消费者对象,默认以class为key
14 */
15 void addConsumer(Object obj);
16
17 /**
18 * 移除消费者
19 * @param obj 消费者对象,默认以class为key
20 */
21 void removeConsumer(Object obj);
22
23 /**
24 * 扫描消费者
25 */
26 void scanConsumer(String packageName);
27 }
28
29 /**
30 * Spring Boot集成EventBus完成消费者自动注册
31 */
32 public abstract class AbstractSpringEventBus implements IEventBus, ApplicationContextAware {
33 private ApplicationContext context;
34
35 @Override
36 public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
37 this.context = applicationContext;
38 this.scanConsumer(null);
39 }
40
41 @Override
42 public void scanConsumer() {
43 context.getBeansOfType(IEventConsumer.class).forEach((k,v) -> this.addConsumer(v));
46 }
47 }
/**
* 消费者需要实现它,Spring Boot集成时扫描该接口的实现注册到EventBus
*/
public interface IEventConsumer<T> {
/**
* 消费者事件
* @param event 事件
*/
void consumer(T event);
}
/**
* 业务实现事件总线
*/
@Component
public class XxxEventBus extends AbstractSpringEventBus implements SubscriberExceptionHandler {
private final EventBus eventBus;
public RecognizeEventBus() {
//异步事件配置线程池
eventBus = new AsyncEventBus(new ThreadPoolExecutor(1, 10, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(20)), this);
}
@Override
public void post(Object event) {
eventBus.post(event);
}
@Override
public void addConsumer(Object obj) {
eventBus.register(obj);
}
@Override
public void removeConsumer(Object obj) {
eventBus.unregister(obj);
}
@Override
public void handleException(Throwable exception, SubscriberExceptionContext context) {
//异常处理
}
}
/**
* 消息投递
*/
@Autowired
private XxxEventBus bus;
...
bus.post(xxxEvent)
...
/**
* 消费者实现,@Subscribe特别重要
*/
@Component
public class XxxConsumer implements IEventConsumer<XxxEvent> {
@Subscribe
@Override
public void consumer(XxxEvent event) {
log.info("event:{}", JSON.toJSONString(event));
}
}
2. Spring Event
熟悉Spring的同学,肯定了解spring自身提供了一套完整的事件监听机制。重点关注的几个类:事件源继承 ApplicationEvent、监听者实现 ApplicationListener<T>、 ApplicationEventPublisher发布事件
1 /**
2 * 继承ApplicationEvent自定义一个事件
3 */
4 public class EventDemo extends ApplicationEvent {
5 //事件消息体
6 private String message;
7
8
9 public EventDemo(Object source, String message) {
10 super(source);
11 this.message = message;
12 }
13
14 public String getMessage() {
15 return message;
16 }
17 }
1 /**
2 * 实现ApplicationListener<T>自定义一个事件监听者
3 */
4 @Component
5 public class EventDemoListener implements ApplicationListener<EventDemo> {
6 @Override
7 public void onApplicationEvent(EventDemo event) {
8 System.out.println("receiver " + event.getMessage());
9 }
10 }
1 /**
2 * ApplicationEventPublisher投递消息
3 */
4 @Autowired
5 private ApplicationEventPublisher applicationEventPublisher;
6 ...
7 EventDemo demo = new EventDemo(this, message);
8 applicationEventPublisher.publishEvent(demo);
9 ...
比较EventBus与Spring Event
- 使用方式比较
项目 | 事件 | 发布者 | 发布方法 | 是否异步 | 监听者 | 注册方式 |
---|---|---|---|---|---|---|
EventBus | 任意对象 | EventBus | EventBus#post | 是 | 注解Subscribe方法 | 手动注册EventBus#register |
Spring Event | 任意对象 | ApplicationEventPublisher | ApplicationEventPublisher#publishEvent | 支持同步异步 | 注解EventListener方法 | 系统注册 |
- 使用场景比较
项目 | 事件区分 | 是否支持事件簇 | 是否支持自定义event | 是否支持过滤 | 是否支持事件隔离 | 复杂程度 |
---|---|---|---|---|---|---|
EventBus | Class | 是 | 是 | 否 | 是 | 简单 |
Spring Event | Class | 是 | 是 | 是 | 否 | 复杂 |