# How to Use Guava's EventBus for Publish-Subscribe Communication

> Learn how to implement publish-subscribe communication in Java using Guava's EventBus. Discover a powerful way for components to interact without direct references.

- Repository: [Google/guava](https://github.com/google/guava)
- Tags: how-to-guide
- Published: 2026-08-08

---

**Guava's EventBus provides an in-process publish-subscribe mechanism that lets Java components communicate without holding explicit references to one another.**

The Google Guava library includes a lightweight yet powerful EventBus implementation that enables decoupled messaging between application layers. This pattern removes tight coupling by allowing objects to publish events and subscribe to specific types without direct dependencies. Learning how to use Guava's EventBus for publish-subscribe communication helps build maintainable Java applications with clean separation of concerns.

## Core Components of Guava EventBus

### The EventBus Class

The `EventBus` class in [`com/google/common/eventbus/EventBus.java`](https://github.com/google/guava/blob/main/com/google/common/eventbus/EventBus.java) serves as the central message hub. It maintains a registry of subscribers and dispatches published events to matching handler methods. According to the source implementation (lines 28-49, 96-110, 124-132), the bus performs runtime type checking to determine which subscribers should receive a given event.

### Subscriber Methods with @Subscribe

Subscribers define public methods annotated with `@Subscribe` from [`com/google/common/eventbus/Subscribe.java`](https://github.com/google/guava/blob/main/com/google/common/eventbus/Subscribe.java). Each subscriber method must accept exactly one argument representing the event type it handles:

```java
import com.google.common.eventbus.Subscribe;

public class OrderListener {
    @Subscribe
    public void onOrderCreated(OrderEvent event) {
        // Process the event
        System.out.println("Order received: " + event.getOrderId());
    }
}

```

The bus uses [`SubscriberRegistry.java`](https://github.com/google/guava/blob/main/SubscriberRegistry.java) to map event types to subscriber methods internally.

### Posting Events

Publish events through the bus using `EventBus#post(Object)`. The bus traverses the class hierarchy to find subscribers whose parameter type is assignable from the event class:

```java
EventBus bus = new EventBus();
bus.register(new OrderListener());

// Publish event - delivered to all matching subscribers
bus.post(new OrderEvent("12345"));  // EventBus.java:58-66

```

## Thread Safety and Execution Model

### Direct Execution by Default

By default, `EventBus` uses `MoreExecutors.directExecutor()`, meaning subscriber methods execute synchronously in the caller's thread. This synchronous behavior ensures ordering but blocks the publisher until all subscribers complete.

### Custom Executors for Asynchronous Processing

Supply a custom `Executor` during construction to offload processing to background threads. The `AsyncEventBus` subclass in [`com/google/common/eventbus/AsyncEventBus.java`](https://github.com/google/guava/blob/main/com/google/common/eventbus/AsyncEventBus.java) provides a convenient constructor for this pattern:

```java
Executor executor = Executors.newFixedThreadPool(4);
EventBus asyncBus = new EventBus(
    "async-bus", 
    executor,
    EventBus.Dispatcher.perThreadDispatchQueue(),
    EventBus.LoggingHandler.INSTANCE
);

```

### Concurrent Event Handling

The bus guarantees that a specific subscriber method is not invoked concurrently for the same event type unless explicitly marked with `@AllowConcurrentEvents`. This annotation allows the bus to invoke the method from multiple threads simultaneously when using a multi-threaded executor.

## Error Handling and Subscriber Exceptions

Exceptions thrown by subscriber methods do not propagate back to the poster. Instead, `EventBus` catches exceptions and delegates them to a `SubscriberExceptionHandler`. The default implementation (`LoggingHandler` inside `EventBus`) logs exceptions to standard error.

You can provide a custom handler to implement circuit breakers, dead letter queues, or monitoring:

```java
EventBus bus = new EventBus(new SubscriberExceptionHandler() {
    @Override
    public void handleException(Throwable exception, SubscriberExceptionContext context) {
        System.err.println("Exception in " + context.getSubscriberMethod() + ": " + exception);
    }
});

```

## Complete Working Example

This example demonstrates the full lifecycle from event definition to consumption:

```java
// 1. Define event payload
public final class UserLoginEvent {
    private final String username;
    public UserLoginEvent(String username) { this.username = username; }
    public String getUsername() { return username; }
}

// 2. Create subscriber
import com.google.common.eventbus.Subscribe;

public class AuditLogger {
    @Subscribe
    public void recordLogin(UserLoginEvent event) {
        System.out.println("AUDIT: User " + event.getUsername() + " logged in");
    }
}

// 3. Wire components together
import com.google.common.eventbus.EventBus;

public class Application {
    public static void main(String[] args) {
        EventBus bus = new EventBus();                           // EventBus.java:64-66
        bus.register(new AuditLogger());                         // EventBus.java:35-37
        
        // Simulate login
        bus.post(new UserLoginEvent("alice"));                   // EventBus.java:58-66
    }
}

```

## Handling Unmatched Events with DeadEvent

When no subscribers match a posted event type, Guava wraps the event in a `DeadEvent` and reposts it. Create a catch-all subscriber to handle orphaned events:

```java
import com.google.common.eventbus.DeadEvent;
import com.google.common.eventbus.Subscribe;

public class DeadEventListener {
    @Subscribe
    public void onDeadEvent(DeadEvent dead) {
        System.out.println("No handler for: " + dead.getEvent());
    }
}

```

Register this listener to monitor for configuration errors or unhandled event types in [`com/google/common/eventbus/DeadEvent.java`](https://github.com/google/guava/blob/main/com/google/common/eventbus/DeadEvent.java).

## Summary

- **EventBus** acts as the central publish-subscribe hub located at [`com/google/common/eventbus/EventBus.java`](https://github.com/google/guava/blob/main/com/google/common/eventbus/EventBus.java), managing registrations and dispatching.
- **@Subscribe** annotation marks handler methods in [`com/google/common/eventbus/Subscribe.java`](https://github.com/google/guava/blob/main/com/google/common/eventbus/Subscribe.java), requiring exactly one parameter of the event type.
- **EventBus#post(Object)** delivers events to all matching subscribers based on type assignability, processing them sequentially by default.
- **Threading** defaults to direct synchronous execution, but custom `Executor` instances or `AsyncEventBus` enable background processing.
- **DeadEvent** captures events with no subscribers, allowing you to log or handle orphaned messages.
- **Exceptions** are isolated from publishers through `SubscriberExceptionHandler`, preventing cascading failures.

## Frequently Asked Questions

### What is the difference between EventBus and AsyncEventBus?

**EventBus** executes subscribers in the caller's thread by default using a direct executor, while **AsyncEventBus** (in [`com/google/common/eventbus/AsyncEventBus.java`](https://github.com/google/guava/blob/main/com/google/common/eventbus/AsyncEventBus.java)) is a convenience subclass that requires you to provide an `Executor` during construction. AsyncEventBus immediately returns control to the poster and processes events asynchronously, though you can achieve the same behavior with the base EventBus by passing a custom executor to its constructor.

### How does EventBus handle subscriber exceptions?

When a subscriber method throws an exception, the bus catches it and passes it to a `SubscriberExceptionHandler`. The default handler logs to standard error without interrupting other subscribers or propagating the exception to the event poster. You can customize this behavior by providing your own handler implementation to the EventBus constructor to implement retry logic, metrics collection, or error reporting.

### Can a subscriber method receive multiple event types?

Yes, a single subscriber class can define multiple `@Subscribe` methods, each accepting different event types. However, each individual subscriber method can only accept one specific event type as its parameter. The bus dispatches each event to all matching methods across all registered subscribers based on the runtime type of the posted object.

### Is EventBus thread-safe for concurrent registration?

Yes, the EventBus implementation is thread-safe. Multiple threads can safely call `register()`, `unregister()`, and `post()` concurrently. The internal `SubscriberRegistry` uses thread-safe collections to manage subscriber mappings. However, remember that unless you use `@AllowConcurrentEvents`, the bus serializes invocations of a specific subscriber method to prevent concurrent execution of the same handler.