Google Guava EventBus — 概念、原理、场景与代码示例

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/异常隔离等特性时使用
相关推荐
谢亮_vipxieliang2 天前
ValidX vs Google Guava Preconditions:验证 vs 断言
java·spring boot·后端·spring cloud·hibernate·guava
吴声子夜歌4 天前
Guava——散列
java·guava
吴声子夜歌4 天前
Guava——并发(二)
java·网络·guava
吴声子夜歌4 天前
Guava——事件总线
java·网络·guava
吴声子夜歌4 天前
Guava——并发(一)
java·guava
吴声子夜歌5 天前
Guava——反射
java·开发语言·guava
吴声子夜歌5 天前
Guava——基本工具(二)
windows·python·guava
吴声子夜歌5 天前
Guava——基本工具(一)
windows·guava
吴声子夜歌6 天前
Guava——集合(二)
java·guava
吴声子夜歌6 天前
Guava——缓存
缓存·guava