Google Guava EventBus --- 概念、原理、场景与代码示例
一、什么是 Guava EventBus
Guava EventBus 是 Google Guava 库提供的一个轻量级进程内事件总线。它让同一个 JVM 内的组件可以通过发布/订阅事件进行通信,不需要显式注册回调接口或直接持有对方的引用。
一句话总结
EventBus = 一个内存中的"公告板"
发布者在公告板上贴通知,所有注册了的订阅者自动看到并处理
Guava 是什么
Google Guava 是 Google 开源的 Java 核心工具库,包含集合、缓存、并发、字符串处理、IO 等常用工具。EventBus 是其中的一个模块。
xml
<!-- Maven 依赖 -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>32.1.3-jre</version>
</dependency>
注:
博客:
https://blog.csdn.net/badao_liumang_qizhi
二、核心概念
| 概念 | 说明 |
|---|---|
| EventBus | 事件总线实例,负责分发事件 |
| @Subscribe | 标注在方法上,表示该方法是事件处理器 |
| post(event) | 发布事件 |
| register(subscriber) | 注册订阅者 |
| unregister(subscriber) | 取消注册 |
| DeadEvent | 没有任何订阅者处理的事件会包装成 DeadEvent |
工作流程
1. 创建 EventBus 实例
2. 订阅者编写带 @Subscribe 注解的方法
3. 将订阅者注册到 EventBus
4. 发布者调用 eventBus.post(event)
5. EventBus 找到所有匹配的 @Subscribe 方法并调用
三、基础用法
最简示例
java
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
// 1. 定义事件(普通 POJO)
public class OrderCreatedEvent {
private final String orderId;
private final String userName;
public OrderCreatedEvent(String orderId, String userName) {
this.orderId = orderId;
this.userName = userName;
}
public String getOrderId() { return orderId; }
public String getUserName() { return userName; }
}
// 2. 定义订阅者
public class SmsSubscriber {
@Subscribe
public void onOrderCreated(OrderCreatedEvent event) {
System.out.println("发短信:" + event.getUserName() + ",您的订单"
+ event.getOrderId() + "已创建");
}
}
public class PointsSubscriber {
@Subscribe
public void onOrderCreated(OrderCreatedEvent event) {
System.out.println("加积分:用户" + event.getUserName() + "加100分");
}
}
// 3. 使用
public class Main {
public static void main(String[] args) {
// 创建事件总线
EventBus eventBus = new EventBus("order-bus");
// 注册订阅者
eventBus.register(new SmsSubscriber());
eventBus.register(new PointsSubscriber());
// 发布事件
eventBus.post(new OrderCreatedEvent("SO001", "张三"));
}
}
// 输出:
// 发短信:张三,您的订单SO001已创建
// 加积分:用户张三加100分
四、同步 vs 异步
同步 EventBus(默认)
java
// 同步:post() 会等所有订阅者执行完才返回
EventBus eventBus = new EventBus("sync-bus");
eventBus.register(new Object() {
@Subscribe
public void handle(String msg) {
// 假设这里耗时3秒
Thread.sleep(3000);
System.out.println("处理完成:" + msg);
}
});
System.out.println("发布前");
eventBus.post("hello"); // 会阻塞3秒
System.out.println("发布后"); // 3秒后才打印
// 输出顺序:
// 发布前
// 处理完成:hello ← 等了3秒
// 发布后
异步 AsyncEventBus
java
import com.google.common.eventbus.AsyncEventBus;
import java.util.concurrent.Executors;
// 异步:post() 立即返回,订阅者在线程池中执行
AsyncEventBus asyncBus = new AsyncEventBus("async-bus",
Executors.newFixedThreadPool(4));
asyncBus.register(new Object() {
@Subscribe
public void handle(String msg) {
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + " 处理完成:" + msg);
}
});
System.out.println("发布前");
asyncBus.post("hello"); // 立即返回
System.out.println("发布后"); // 立刻打印
// 输出顺序:
// 发布前
// 发布后 ← 立即返回
// pool-1-thread-1 处理完成:hello ← 3秒后在异步线程中完成
五、事件继承
Guava EventBus 支持事件类型的继承匹配------订阅父类事件的处理器也会收到子类事件:
java
// 事件继承体系
public class BaseEvent {
private final String source;
public BaseEvent(String source) { this.source = source; }
public String getSource() { return source; }
}
public class OrderEvent extends BaseEvent {
private final String orderId;
public OrderEvent(String source, String orderId) {
super(source);
this.orderId = orderId;
}
public String getOrderId() { return orderId; }
}
public class PaymentEvent extends BaseEvent {
private final BigDecimal amount;
public PaymentEvent(String source, BigDecimal amount) {
super(source);
this.amount = amount;
}
public BigDecimal getAmount() { return amount; }
}
// 订阅者
public class AuditSubscriber {
// 订阅BaseEvent → 所有子类事件都能收到
@Subscribe
public void onAnyEvent(BaseEvent event) {
System.out.println("审计日志:收到事件来自 " + event.getSource());
}
}
public class OrderSubscriber {
// 只订阅OrderEvent
@Subscribe
public void onOrder(OrderEvent event) {
System.out.println("处理订单:" + event.getOrderId());
}
}
// 使用
EventBus bus = new EventBus();
bus.register(new AuditSubscriber());
bus.register(new OrderSubscriber());
bus.post(new OrderEvent("web", "SO001"));
// 输出:
// 审计日志:收到事件来自 web ← AuditSubscriber 收到(因为OrderEvent继承BaseEvent)
// 处理订单:SO001 ← OrderSubscriber 收到
bus.post(new PaymentEvent("app", new BigDecimal("99.9")));
// 输出:
// 审计日志:收到事件来自 app ← AuditSubscriber 收到
// ← OrderSubscriber 不收到(PaymentEvent ≠ OrderEvent)
六、DeadEvent --- 无人处理的事件
当一个事件发布后没有任何 @Subscribe 方法能处理它时,EventBus 会将其包装为 DeadEvent:
java
import com.google.common.eventbus.DeadEvent;
public class DeadEventHandler {
@Subscribe
public void onDeadEvent(DeadEvent deadEvent) {
System.out.println("警告:没有处理器的事件 → " + deadEvent.getEvent());
System.out.println("来源EventBus:" + deadEvent.getSource());
}
}
EventBus bus = new EventBus();
bus.register(new DeadEventHandler());
// 发布一个没人订阅的事件
bus.post(new SomeUnhandledEvent());
// 输出:警告:没有处理器的事件 → SomeUnhandledEvent@xxxx
这对调试很有用------可以发现被遗忘的事件或拼写错误的订阅方法。
七、异常处理
默认行为
同步 EventBus 中,如果一个订阅者抛出异常,默认会打印日志但继续分发给其他订阅者。
自定义异常处理器
java
EventBus bus = new EventBus(new SubscriberExceptionHandler() {
@Override
public void handleException(Throwable exception, SubscriberExceptionContext context) {
System.err.println("订阅者异常!");
System.err.println("事件:" + context.getEvent());
System.err.println("订阅者:" + context.getSubscriber());
System.err.println("方法:" + context.getSubscriberMethod());
exception.printStackTrace();
// 可以在这里发告警、记日志等
}
});
bus.register(new Object() {
@Subscribe
public void handle(String msg) {
throw new RuntimeException("处理失败!");
}
});
bus.register(new Object() {
@Subscribe
public void handle(String msg) {
System.out.println("我还是能收到:" + msg); // 不受上面异常影响
}
});
bus.post("test");
// 输出:
// 订阅者异常!
// 事件:test
// ...
// 我还是能收到:test ← 其他订阅者正常执行
八、在 Spring 项目中集成
配置
java
@Configuration
public class EventBusConfig {
@Bean
public EventBus syncEventBus() {
return new EventBus("sync-bus");
}
@Bean
public AsyncEventBus asyncEventBus() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("event-");
executor.initialize();
return new AsyncEventBus("async-bus", executor);
}
}
自动注册所有订阅者
java
/**
* 自动将所有标记了 @EventSubscriber 的 Bean 注册到 EventBus.
*/
@Component
public class EventBusAutoRegister implements ApplicationListener<ContextRefreshedEvent> {
@Resource
private EventBus syncEventBus;
@Resource
private AsyncEventBus asyncEventBus;
@Resource
private ApplicationContext applicationContext;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// 扫描所有带 @EventSubscriber 注解的 Bean
Map<String, Object> subscribers =
applicationContext.getBeansWithAnnotation(EventSubscriber.class);
for (Object subscriber : subscribers.values()) {
EventSubscriber annotation = subscriber.getClass().getAnnotation(EventSubscriber.class);
if (annotation.async()) {
asyncEventBus.register(subscriber);
} else {
syncEventBus.register(subscriber);
}
}
}
}
// 自定义注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component // 让Spring扫描到
public @interface EventSubscriber {
boolean async() default false;
}
使用
java
// 订阅者 --- 加上自定义注解即可自动注册
@EventSubscriber(async = true)
public class OrderNotifySubscriber {
@Resource
private SmsService smsService;
@Subscribe
public void onOrderCreated(OrderCreatedEvent event) {
smsService.send(event.getPhone(), "下单成功");
}
}
@EventSubscriber
public class OrderLogSubscriber {
@Subscribe
public void onOrderCreated(OrderCreatedEvent event) {
log.info("订单创建, orderId:{}", event.getOrderId());
}
}
// 发布者
@Service
public class OrderService {
@Resource
private EventBus syncEventBus;
public void createOrder(OrderDto dto) {
Order order = orderRepository.save(dto);
syncEventBus.post(new OrderCreatedEvent(order));
}
}
九、一个订阅者处理多种事件
一个类中可以有多个 @Subscribe 方法,根据参数类型自动匹配:
java
@EventSubscriber
public class NotificationSubscriber {
@Subscribe
public void onOrderCreated(OrderCreatedEvent event) {
sendSms(event.getPhone(), "下单成功,订单号:" + event.getOrderId());
}
@Subscribe
public void onOrderShipped(OrderShippedEvent event) {
sendSms(event.getPhone(), "已发货,物流单号:" + event.getTrackingNo());
}
@Subscribe
public void onOrderDelivered(OrderDeliveredEvent event) {
sendSms(event.getPhone(), "已签收,请确认收货");
}
@Subscribe
public void onPaymentFailed(PaymentFailedEvent event) {
sendSms(event.getPhone(), "支付失败,请重试");
}
}
十、Guava EventBus vs Spring Event
| 维度 | Guava EventBus | Spring Event |
|---|---|---|
| 依赖 | guava 库 | spring-context |
| 注解 | @Subscribe |
@EventListener |
| 注册方式 | 手动 register() |
自动扫描 |
| 事务感知 | ❌ 无 | ✅ @TransactionalEventListener |
| 异步支持 | AsyncEventBus |
@Async |
| 事件继承 | ✅ 自动匹配父类 | ✅ 支持 |
| 条件过滤 | ❌ 需自行判断 | ✅ condition 属性 |
| DeadEvent | ✅ 有 | ❌ 无 |
| 异常隔离 | ✅ 一个失败不影响其他 | ❌ 默认会传播(同步时) |
| 使用场景 | 非Spring项目或需要精细控制 | Spring项目(推荐) |
选择建议
已有Spring项目 → 直接用 Spring Event(@EventListener)
- 无需额外依赖
- 自动注册
- 支持事务感知
非Spring项目 / 需要更轻量 → 用 Guava EventBus
- 不依赖Spring容器
- 有 DeadEvent 机制
- 异常隔离更好
十一、注意事项与陷阱
1. @Subscribe 方法必须是 public
java
// ✗ 错误:private方法不会被识别
@Subscribe
private void handle(MyEvent event) { }
// ✓ 正确
@Subscribe
public void handle(MyEvent event) { }
2. 参数类型决定订阅什么事件
java
// 订阅 String 类型的事件
@Subscribe
public void handle(String msg) { }
// 订阅 OrderEvent 类型的事件
@Subscribe
public void handle(OrderEvent event) { }
// ✗ 不能有多个参数
@Subscribe
public void handle(OrderEvent event, String extra) { } // 编译不报错但运行时不生效
3. 同步 EventBus 中不要做耗时操作
java
// ✗ 差:阻塞了发布者线程
@Subscribe
public void handle(OrderEvent event) {
Thread.sleep(10000); // 发布者会被阻塞10秒
sendEmail(event);
}
// ✓ 好:用 AsyncEventBus 或内部异步处理
@Subscribe
public void handle(OrderEvent event) {
CompletableFuture.runAsync(() -> sendEmail(event));
}
4. 忘记 register 是最常见的bug
java
EventBus bus = new EventBus();
// 忘记 bus.register(subscriber) → post 后没有任何反应
bus.post(new MyEvent()); // 静悄悄,没人处理 → 变成 DeadEvent
十二、实际项目中的完整示例
java
/**
* 电商系统中使用 Guava EventBus 解耦下单后的多个操作.
*/
// === 事件定义 ===
@Data
@AllArgsConstructor
public class OrderCreatedEvent {
private String orderId;
private Integer userId;
private BigDecimal amount;
private String phone;
private List<String> productNames;
}
// === EventBus 配置 ===
@Configuration
public class EventBusConfig {
@Bean
public AsyncEventBus orderEventBus() {
return new AsyncEventBus("order-events",
Executors.newFixedThreadPool(4, new ThreadFactoryBuilder()
.setNameFormat("order-event-%d").build()));
}
}
// === 订阅者们 ===
@Component
public class OrderSmsSubscriber {
@Resource private SmsService smsService;
@Subscribe
public void handle(OrderCreatedEvent event) {
smsService.send(event.getPhone(),
String.format("下单成功!订单号%s,金额%.2f元",
event.getOrderId(), event.getAmount()));
}
}
@Component
public class OrderPointsSubscriber {
@Resource private PointsService pointsService;
@Subscribe
public void handle(OrderCreatedEvent event) {
int points = event.getAmount().intValue(); // 1元=1积分
pointsService.add(event.getUserId(), points);
}
}
@Component
public class OrderStatisticsSubscriber {
@Resource private StatisticsService statisticsService;
@Subscribe
public void handle(OrderCreatedEvent event) {
statisticsService.incrementDailyOrderCount();
statisticsService.addDailyRevenue(event.getAmount());
}
}
@Component
public class OrderInventorySubscriber {
@Resource private InventoryService inventoryService;
@Subscribe
public void handle(OrderCreatedEvent event) {
for (String product : event.getProductNames()) {
inventoryService.decrementStock(product, 1);
}
}
}
// === 自动注册 ===
@Component
public class OrderEventBusRegistrar {
@Resource private AsyncEventBus orderEventBus;
@Resource private OrderSmsSubscriber smsSubscriber;
@Resource private OrderPointsSubscriber pointsSubscriber;
@Resource private OrderStatisticsSubscriber statsSubscriber;
@Resource private OrderInventorySubscriber inventorySubscriber;
@PostConstruct
public void init() {
orderEventBus.register(smsSubscriber);
orderEventBus.register(pointsSubscriber);
orderEventBus.register(statsSubscriber);
orderEventBus.register(inventorySubscriber);
}
}
// === 发布者(订单服务) ===
@Service
public class OrderService {
@Resource private OrderRepository orderRepository;
@Resource private AsyncEventBus orderEventBus;
@Transactional
public Order createOrder(OrderDto dto) {
Order order = orderRepository.save(dto);
// 发布事件 --- 不关心后续处理
orderEventBus.post(new OrderCreatedEvent(
order.getId(), order.getUserId(), order.getAmount(),
order.getPhone(), order.getProductNames()));
return order;
}
// 未来新增"下单后推送APP通知"?
// 只需要新增一个 Subscriber + register,OrderService 零修改
}
十三、总结
Guava EventBus 是什么:
Google Guava 提供的进程内轻量级事件总线
通过 @Subscribe 注解声明订阅者
通过 post() 发布事件
核心特点:
轻量(几个类就实现了)
进程内(不能跨JVM)
支持同步和异步
事件继承匹配
订阅者异常隔离
DeadEvent 检测未处理事件
适用场景:
同一应用内模块解耦
一个操作需要触发多个后续处理
需要灵活增减后续处理逻辑
不适用场景:
跨服务通信(用MQ)
需要事务感知(用Spring Event)
需要消息持久化(用MQ)
在Spring项目中:
优先用 Spring Event(@EventListener)
Guava EventBus 更适合非Spring环境或需要DeadEvent/异常隔离等特性时使用