前言
EventBus 是 Guava 的事件处理机制,是观察者模式(生产/消费模型)的一种实现。
观察者模式在我们日常开发中使用非常广泛,例如在订单系统中,订单状态或者物流信息的变更会向用户发送APP推送、短信、通知卖家、买家等等;审批系统中,审批单的流程流转会通知发起审批用户、审批的领导等等。
Observer模式也是 JDK 中自带就支持的,其在 1.0 版本就已经存在 Observer,不过随着 Java 版本的飞速升级,其使用方式一直没有变化,许多程序库提供了更加简单的实现,例如 Guava EventBus、RxJava、EventBus 等
一、为什么要用 Observer模式以及 EventBus 优点 ?
EventBus 优点
- 相比 Observer 编程简单方便
- 通过自定义参数可实现同步、异步操作以及异常处理
- 单进程使用,无网络影响
缺点
- 只能单进程使用
- 项目异常重启或者退出不保证消息持久化
如果需要分布式使用还是需要使用 MQ
二、EventBus 使用步骤
导入对应的依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
</dependency>
编写线程池配置
@Data
@ConfigurationProperties(prefix = "eventbus.pool")
public class EventBusExecutorProperites {
/**
* 核心线程数
*/
private int corePoolSize = 5;
/**
* 最大线程数
*/
private int maxPoolSize = 20;
/**
* 空闲线程销毁时间
*/
private int keepAliveSeconds = 60;
/**
* 任务队列容量
*/
private int queueCapacity = 1000;
/**
* 是否允许核心线程空闲退出
*/
private boolean allowCoreThreadTimeOut = false;
/**
* 线程前缀
*/
private String threadNamePrefix = "event-bus-pool-";
}
EventBus中事件订阅者的方法必需只能接受一个参数,声明基础参数类
@Data
public abstract class BaseCallableBean {
private String oper;
private String opeTime;
}
声明订阅者基础类
public abstract class BaseCallable<T extends BaseCallableBean> {
public abstract void call(T t);
}
定义了事件监听者类,并定义参数类
注意:使用@Subscribe注解
@Data
@ToString
@AllArgsConstructor
public class TestBean extends BaseCallableBean{
private String id;
private String name;
}
@Slf4j
@Component
public class TestBaseCallable extends BaseCallable<TestBean> {
@Override
@Subscribe
public void call(TestBean testBean) {
log.info("hello");
log.info(testBean.toString());
}
}
进行EventBus配置
@Slf4j
@Configuration
@EnableConfigurationProperties(EventBusExecutorProperites.class)
public class EventBusConfiguration implements ApplicationListener<ContextRefreshedEvent> {
@Autowired
private EventBusExecutorProperites executorProperites;
@Bean("eventBusExecutor")
@Primary
public Executor getThreadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心线程
executor.setCorePoolSize(executorProperites.getCorePoolSize());
// 最大线程数
executor.setMaxPoolSize(executorProperites.getMaxPoolSize());
// 任务队列容量
executor.setQueueCapacity(executorProperites.getQueueCapacity());
// 线程前缀
executor.setThreadNamePrefix(executorProperites.getThreadNamePrefix());
// 是否允许核心线程空闲退出
executor.setAllowCoreThreadTimeOut(executorProperites.isAllowCoreThreadTimeOut());
// 空闲线程销毁时间
executor.setKeepAliveSeconds(executorProperites.getKeepAliveSeconds());
// 任务装饰器-增强器,类似于aop
executor.setTaskDecorator(new MDCTaskDecorator());
// 不丢弃任务 由调用线程处理该任务
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 如果为true 线程池shutdown false 为 shutdownNow
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.initialize();
return executor;
}
static class MDCTaskDecorator implements TaskDecorator {
@Override
public Runnable decorate(Runnable runnable) {
// 获取当前线程的MDC上下文数据副本,并存储在 mdcContext 中。
Map<String, String> contextMap = MDC.getCopyOfContextMap();
return () -> {
if (contextMap != null) {
// 将 mdcContext 中的数据重新设置回当前线程的MDC,以恢复或传递上下文数据给其他线程或任务。
MDC.setContextMap(contextMap);
}
runnable.run();
};
}
}
@Bean("eventBus")
public EventBus eventBus() {
return new AsyncEventBus(getThreadPoolTaskExecutor());
}
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
registerSubscribes(event.getApplicationContext());
}
private void registerSubscribes(ApplicationContext context) {
if (null == context) {
return;
}
// 根据上下文获取类型为 BaseCallable 的bean
String[] beanDefinitionNames = context.getBeanNamesForType(BaseCallable.class, false, true);
// 根据上下文获取eventbus的bean,用于注册
EventBus eventBus = context.getBean(EventBus.class);
for (String beanDefinitionName : beanDefinitionNames) {
Object bean = context.getBean(beanDefinitionName);
try {
Class<?> aClass = bean.getClass();
Method call = aClass.getMethod("call", BaseCallableBean.class);
if (null == call) {
continue;
}
log.info("EventBus method-Subscribe register bean[{}]", beanDefinitionName);
} catch (NoSuchMethodException e) {
log.info("EventBus method-Subscribe register bean Exception : {}", e.getMessage());
e.printStackTrace();
}
eventBus.register(bean);
}
}
}
测试
@RestController
@SpringBootApplication
public class DemoApplication {
@Autowired
private EventBus eventBus;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@GetMapping("testEventBus")
public String testEventBus(){
TestBean testBean = new TestBean("1","hello");
eventBus.post(testBean);
return "ok";
}
}
测试结果