事件总线
-
- 1、概述
- 2、核心API
- 3、使用示例
-
- [3.1、基础同步 EventBus 使用示例](#3.1、基础同步 EventBus 使用示例)
- [3.2、异步 AsyncEventBus 与异常处理](#3.2、异步 AsyncEventBus 与异常处理)
- 4、进阶示例
-
- [4.1、事件继承分发机制(Polymorphic Dispatching)](#4.1、事件继承分发机制(Polymorphic Dispatching))
- [4.2、高并发异步总线与 @AllowConcurrentEvents](#4.2、高并发异步总线与 @AllowConcurrentEvents)
- 4.3、真实业务场景应用
1、概述
Guava EventBus 是 Google Guava 提供的进程内发布-订阅(Pub/Sub)事件总线,通过注解和反射机制实现组件解耦,避免了编写大量观察者模式接口模板代码。
| 类 / 注解 / 接口 | 类型 | 说明 |
|---|---|---|
EventBus |
类 | 同步事件总线。事件发布(post)与订阅者执行在同一个线程中同步运行 |
AsyncEventBus |
类 | 异步事件总线(继承自 EventBus)。配合线程池 Executor,异步分发和执行订阅逻辑 |
@Subscribe |
注解 | 标记在订阅者方法上。方法必须是 public void 且只能包含一个参数(即监听的事件类型) |
@AllowConcurrentEvents |
注解 | 配合 AsyncEventBus 使用。标记方法是线程安全的,允许总线并发调用该方法而无需加锁 |
DeadEvent |
类 | 当发布的事件在总线中找不到任何匹配的订阅者时,会被包装为 DeadEvent 重新发布 |
SubscriberExceptionHandler |
接口 | 订阅者方法执行抛出未捕获异常时的自定义处理器 |
EventBus 核心方法
- register(Object object):注册监听者对象(自动扫描对象内所有带有 @Subscribe 的方法)。
- unregister(Object object):注销监听者对象。
- post(Object event):向总线发布事件,自动匹配并触发消费该事件(及其父类/接口)的所有订阅者。
2、核心API
1. API 全景与方法签名详解
EventBus 与 AsyncEventBus 构造方法与 API
| 类名 | 方法 / 构造器 | 作用与细节 |
|---|---|---|
| EventBus | EventBus() |
创建标识符为 "default" 的同步总线 |
EventBus(String identifier) |
创建指定名称的同步总线(方便日志定位) | |
EventBus(SubscriberExceptionHandler handler) |
传入自定义异常处理器创建总线 | |
register(Object listener) |
扫描目标对象中所有带 @Subscribe 的 public 方法并注册 |
|
unregister(Object listener) |
注销指定对象的监听(若未注册过会抛 IllegalArgumentException) |
|
post(Object event) |
同步发布事件;按注册顺序在当前线程依次调用匹配的订阅者 | |
identifier() |
获取总线的名称/标识符 | |
| AsyncEventBus | AsyncEventBus(String id, Executor executor) |
指定标识符和执行线程池 |
AsyncEventBus(Executor executor) |
匿名异步总线,使用指定线程池调度事件 | |
AsyncEventBus(Executor exec, SubscriberExceptionHandler handler) |
指定线程池与异常处理器的异步总线 |
2. 异常追踪与死事件 API
| 类名 | 方法签名 | 作用说明 |
|---|---|---|
| SubscriberExceptionContext | EventBus getEventBus() |
获取抛出异常时所在的总线对象 |
Object getEvent() |
获取当前引发异常的事件实例 | |
Object getSubscriber() |
获取当前抛出异常的订阅者监听类对象 | |
Method getSubscriberMethod() |
获取触发异常的 java.lang.reflect.Method 实例 |
|
| DeadEvent | Object getSource() |
获取发送该未消费事件 processing 的 EventBus 实例 |
Object getEvent() |
获取原先发送但未被任何订阅者接收的原始事件对象 |
3、使用示例
3.1、基础同步 EventBus 使用示例
步骤 1:定义事件对象(普通的 POJO)
java
// 订单创建事件
public class OrderCreatedEvent {
private final String orderId;
public OrderCreatedEvent(String orderId) {
this.orderId = orderId;
}
public String getOrderId() {
return orderId;
}
}
步骤 2:定义订阅者(Listener)
java
import com.google.common.eventbus.DeadEvent;
import com.google.common.eventbus.Subscribe;
public class OrderEventListener {
// 监听特定订单创建事件
@Subscribe
public void onOrderCreated(OrderCreatedEvent event) {
System.out.println("[邮件服务] 收到订单创建事件,发送确认邮件,订单号: " + event.getOrderId());
}
// 监听死事件(没有找到任何匹配订阅者的事件)
@Subscribe
public void handleDeadEvent(DeadEvent deadEvent) {
System.out.println("警告:收到死事件,未被任何订阅者消费: " + deadEvent.getEvent());
}
}
步骤 3:注册并发布事件
java
import com.google.common.eventbus.EventBus;
public class EventBusMain {
public static void main(String[] args) {
// 1. 创建同步事件总线
EventBus eventBus = new EventBus("OrderBus");
// 2. 注册监听者
OrderEventListener listener = new OrderEventListener();
eventBus.register(listener);
// 3. 发布事件(匹配到 onOrderCreated 方法执行)
eventBus.post(new OrderCreatedEvent("ORD_10001"));
// 4. 发布未配置订阅者的事件(会触发 DeadEvent)
eventBus.post("Unsubscribed String Event");
// 5. 注销监听者
eventBus.unregister(listener);
}
}
java
[邮件服务] 收到订单创建事件,发送确认邮件,订单号: ORD_10001
警告:收到死事件,未被任何订阅者消费: Unsubscribed String Event
3.2、异步 AsyncEventBus 与异常处理
在生产环境中,消费逻辑耗时较长时推荐使用 AsyncEventBus,并显式配置 SubscriberExceptionHandler 捕获订阅者内部抛出的运行时异常。
java
import com.google.common.eventbus.AsyncEventBus;
import com.google.common.eventbus.Subscribe;
import com.google.common.eventbus.SubscriberExceptionHandler;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class AsyncEventBusExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(4);
// 自定义订阅者未捕获异常处理器
SubscriberExceptionHandler exceptionHandler = (exception, context) -> {
System.err.printf("执行订阅者方法 [%s] 抛出异常,事件内容: %s,原因: %s%n",
context.getSubscriberMethod().getName(),
context.getEvent(),
exception.getMessage());
};
// 创建异步事件总线
AsyncEventBus asyncEventBus = new AsyncEventBus("AsyncOrderBus", executor, exceptionHandler);
// 注册会触发异常的订阅者
asyncEventBus.register(new Object() {
@Subscribe
public void handle(String message) {
System.out.println("异步处理线程: " + Thread.currentThread().getName());
throw new RuntimeException("模拟处理失败");
}
});
// 发布消息
asyncEventBus.post("Async Test Message");
executor.shutdown();
}
}
4、进阶示例
4.1、事件继承分发机制(Polymorphic Dispatching)
Guava EventBus 支持多态分发:当发布一个子类事件时,订阅了该子类及其父类或实现接口的所有订阅者都会按顺序收到该事件。
java
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
// 1. 定义事件继承结构
interface LoggableEvent { String getMessage(); }
class BaseOrderEvent implements LoggableEvent {
private final String orderId;
public BaseOrderEvent(String orderId) { this.orderId = orderId; }
public String getOrderId() { return orderId; }
@Override public String getMessage() { return "Order " + orderId; }
}
class PaySuccessEvent extends BaseOrderEvent {
private final double amount;
public PaySuccessEvent(String orderId, double amount) {
super(orderId);
this.amount = amount;
}
public double getAmount() { return amount; }
}
// 2. 订阅者定义不同粒度的监听方法
class MultiEventListener {
// 监听所有实现 LoggableEvent 接口的事件
@Subscribe
public void onAnyLoggable(LoggableEvent event) {
System.out.println("[日志审计中心] 记录通用事件: " + event.getMessage());
}
// 监听所有订单基础事件
@Subscribe
public void onBaseOrder(BaseOrderEvent event) {
System.out.println("[订单中心] 触发订单通用处理,单号: " + event.getOrderId());
}
// 仅监听支付成功事件
@Subscribe
public void onPaySuccess(PaySuccessEvent event) {
System.out.println("[财务中心] 收到支付成功,金额: " + event.getAmount());
}
}
public class InheritanceExample {
public static void main(String[] args) {
EventBus bus = new EventBus("InheritanceBus");
bus.register(new MultiEventListener());
// 发布子类 PaySuccessEvent 消息
// 会依次触发:onPaySuccess -> onBaseOrder -> onAnyLoggable
bus.post(new PaySuccessEvent("ORD-2026-001", 299.0));
}
}
4.2、高并发异步总线与 @AllowConcurrentEvents
在 AsyncEventBus 下,默认对同一个监听者对象的订阅方法调用会加锁串行化。若订阅者方法是线程安全的,可加上 @AllowConcurrentEvents 注解来解除锁限制,提升高并发吞吐量。
java
import com.google.common.eventbus.AllowConcurrentEvents;
import com.google.common.eventbus.AsyncEventBus;
import com.google.common.eventbus.Subscribe;
import com.google.common.eventbus.SubscriberExceptionHandler;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
public class HighConcurrencyExample {
// 无状态/线程安全的订阅者
static class MetricsCollector {
private final AtomicInteger counter = new AtomicInteger();
@Subscribe
@AllowConcurrentEvents // 允许线程池中的多条线程同时并发执行此方法,提高吞吐量
public void onMetric(String metricName) {
int current = counter.incrementAndGet();
System.out.println(Thread.currentThread().getName() + " 处理指标: " + metricName + " | 当前总计: " + current);
}
}
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(4);
// 自定义详细的异常日志上下文提取
SubscriberExceptionHandler exceptionHandler = (exception, context) -> {
System.err.println("--- 事件处理异常 ---");
System.err.println("总线标识: " + context.getEventBus().identifier());
System.err.println("发生类: " + context.getSubscriber().getClass().getName());
System.err.println("发生方法: " + context.getSubscriberMethod().getName());
System.err.println("事件对象: " + context.getEvent());
exception.printStackTrace();
};
AsyncEventBus asyncBus = new AsyncEventBus("MetricsBus", threadPool, exceptionHandler);
asyncBus.register(new MetricsCollector());
// 模拟多线程同时向总线 post 消息
for (int i = 0; i < 5; i++) {
final int index = i;
new Thread(() -> asyncBus.post("qps_metric_" + index)).start();
}
threadPool.shutdown();
}
}
4.3、真实业务场景应用
通过 EventBus 将"创建订单"主逻辑与"发送短信"、"更新库存"、"发放积分"等旁路业务解耦:
java
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
class OrderService {
private final EventBus eventBus;
public OrderService(EventBus eventBus) {
this.eventBus = eventBus;
}
public void createOrder(String orderId, String userId, long amount) {
// 1. 执行核心业务逻辑:写订单数据库
System.out.println(">>> 主流程:订单 " + orderId + " 创建成功,保存数据库");
// 2. 发布事件解耦旁路逻辑
eventBus.post(new OrderCompletedEvent(orderId, userId, amount));
}
}
// 旁路服务 1:短信通知
class SmsNotificationService {
@Subscribe
public void sendSms(OrderCompletedEvent event) {
System.out.println("旁路服务:向用户 " + event.userId() + " 发送短信通知");
}
}
// 旁路服务 2:积分扣减
class PointsService {
@Subscribe
public void addPoints(OrderCompletedEvent event) {
System.out.println("旁路服务:用户 " + event.userId() + " 增加积分: " + (event.amount() / 10));
}
}
record OrderCompletedEvent(String orderId, String userId, long amount) {}
public class DomainDecoupleDemo {
public static void main(String[] args) {
EventBus bus = new EventBus("DomainBus");
// 注册各种独立微服务/组件
bus.register(new SmsNotificationService());
bus.register(new PointsService());
OrderService orderService = new OrderService(bus);
orderService.createOrder("ORD_20260903", "USER_888", 50000);
}
}