Published on Fri May 16 2025 12:00:00 GMT+0000 (Coordinated Universal Time) by Purusothaman Ramanujam
Google Guava EventBus: Simple Publish-Subscribe Pattern
Introduction
Google Guava’s EventBus provides a simple and effective way to implement the publish-subscribe pattern in Java applications. It allows components to communicate without direct dependencies, promoting loose coupling and event-driven architecture.
In this post, we’ll explore how to use EventBus for event handling and communication between components.
Adding Guava to Your Project
Maven Dependency
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>32.1.3-jre</version></dependency>
Gradle Dependency
implementation 'com.google.guava:guava:32.1.3-jre'
Basic EventBus Usage
EventBus allows you to post events and have them handled by registered subscribers:
import com.google.common.eventbus.EventBus;import com.google.common.eventbus.Subscribe;
public class BasicEventBusExample { public static class MessageEvent { private final String message;
public MessageEvent(String message) { this.message = message; }
public String getMessage() { return message; } }
public static class MessageHandler { @Subscribe public void handleMessage(MessageEvent event) { System.out.println("Received message: " + event.getMessage()); }
@Subscribe public void handleString(String message) { System.out.println("Received string: " + message); } }
public static void main(String[] args) { EventBus eventBus = new EventBus(); MessageHandler handler = new MessageHandler();
// Register handler eventBus.register(handler);
// Post events eventBus.post(new MessageEvent("Hello, EventBus!")); eventBus.post("Simple string message");
// Unregister handler eventBus.unregister(handler); }}
Event Types and Handlers
Multiple Event Types
public class MultipleEventTypesExample { public static class UserEvent { private final String userId; private final String action;
public UserEvent(String userId, String action) { this.userId = userId; this.action = action; }
public String getUserId() { return userId; } public String getAction() { return action; } }
public static class OrderEvent { private final String orderId; private final double amount;
public OrderEvent(String orderId, double amount) { this.orderId = orderId; this.amount = amount; }
public String getOrderId() { return orderId; } public double getAmount() { return amount; } }
public static class EventHandler { @Subscribe public void handleUserEvent(UserEvent event) { System.out.println("User event: " + event.getUserId() + " - " + event.getAction()); }
@Subscribe public void handleOrderEvent(OrderEvent event) { System.out.println("Order event: " + event.getOrderId() + " - $" + event.getAmount()); }
@Subscribe public void handleStringEvent(String event) { System.out.println("String event: " + event); } }
public static void main(String[] args) { EventBus eventBus = new EventBus(); EventHandler handler = new EventHandler();
eventBus.register(handler);
// Post different types of events eventBus.post(new UserEvent("user123", "login")); eventBus.post(new OrderEvent("order456", 99.99)); eventBus.post("System notification");
eventBus.unregister(handler); }}
Multiple Handlers
public class MultipleHandlersExample { public static class NotificationEvent { private final String message; private final String type;
public NotificationEvent(String message, String type) { this.message = message; this.type = type; }
public String getMessage() { return message; } public String getType() { return type; } }
public static class EmailHandler { @Subscribe public void handleNotification(NotificationEvent event) { if ("email".equals(event.getType())) { System.out.println("Sending email: " + event.getMessage()); } } }
public static class SMSHandler { @Subscribe public void handleNotification(NotificationEvent event) { if ("sms".equals(event.getType())) { System.out.println("Sending SMS: " + event.getMessage()); } } }
public static class LoggingHandler { @Subscribe public void handleNotification(NotificationEvent event) { System.out.println("Logging notification: " + event.getType() + " - " + event.getMessage()); } }
public static void main(String[] args) { EventBus eventBus = new EventBus();
EmailHandler emailHandler = new EmailHandler(); SMSHandler smsHandler = new SMSHandler(); LoggingHandler loggingHandler = new LoggingHandler();
// Register all handlers eventBus.register(emailHandler); eventBus.register(smsHandler); eventBus.register(loggingHandler);
// Post events eventBus.post(new NotificationEvent("Welcome!", "email")); eventBus.post(new NotificationEvent("Order confirmed", "sms"));
// Unregister handlers eventBus.unregister(emailHandler); eventBus.unregister(smsHandler); eventBus.unregister(loggingHandler); }}
AsyncEventBus
For asynchronous event processing, use AsyncEventBus:
import com.google.common.eventbus.AsyncEventBus;import java.util.concurrent.Executors;
public class AsyncEventBusExample { public static class AsyncEventHandler { @Subscribe public void handleAsyncEvent(String event) { System.out.println("Async handler: " + event + " on thread: " + Thread.currentThread().getName()); // Simulate some processing time try { Thread.sleep(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }
public static void main(String[] args) { // Create AsyncEventBus with custom executor AsyncEventBus eventBus = new AsyncEventBus(Executors.newFixedThreadPool(4)); AsyncEventHandler handler = new AsyncEventHandler();
eventBus.register(handler);
// Post events asynchronously for (int i = 1; i <= 5; i++) { eventBus.post("Event " + i); }
System.out.println("All events posted, they will be processed asynchronously");
// Wait for processing to complete try { Thread.sleep(2000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
eventBus.unregister(handler); }}
Error Handling
EventBus provides error handling through DeadEvent and custom error handlers:
import com.google.common.eventbus.DeadEvent;import com.google.common.eventbus.Subscribe;
public class ErrorHandlingExample { public static class ErrorHandler { @Subscribe public void handleDeadEvent(DeadEvent deadEvent) { System.out.println("No handler found for event: " + deadEvent.getEvent()); } }
public static class FailingHandler { @Subscribe public void handleEvent(String event) { if ("fail".equals(event)) { throw new RuntimeException("Intentional failure"); } System.out.println("Processed: " + event); } }
public static void main(String[] args) { EventBus eventBus = new EventBus(); ErrorHandler errorHandler = new ErrorHandler(); FailingHandler failingHandler = new FailingHandler();
eventBus.register(errorHandler); eventBus.register(failingHandler);
// This will be handled normally eventBus.post("normal event");
// This will cause an exception eventBus.post("fail");
// This will become a DeadEvent eventBus.post(123);
eventBus.unregister(errorHandler); eventBus.unregister(failingHandler); }}
Practical Examples
User Management System
public class UserManagementSystem { public static class User { private String id; private String name; private String email;
public User(String id, String name, String email) { this.id = id; this.name = name; this.email = email; }
// Getters public String getId() { return id; } public String getName() { return name; } public String getEmail() { return email; } }
public static class UserCreatedEvent { private final User user;
public UserCreatedEvent(User user) { this.user = user; }
public User getUser() { return user; } }
public static class UserDeletedEvent { private final String userId;
public UserDeletedEvent(String userId) { this.userId = userId; }
public String getUserId() { return userId; } }
public static class EmailService { @Subscribe public void handleUserCreated(UserCreatedEvent event) { User user = event.getUser(); System.out.println("Sending welcome email to: " + user.getEmail()); } }
public static class AuditService { @Subscribe public void handleUserCreated(UserCreatedEvent event) { User user = event.getUser(); System.out.println("Audit: User created - " + user.getId() + " (" + user.getName() + ")"); }
@Subscribe public void handleUserDeleted(UserDeletedEvent event) { System.out.println("Audit: User deleted - " + event.getUserId()); } }
public static class UserService { private final EventBus eventBus;
public UserService(EventBus eventBus) { this.eventBus = eventBus; }
public void createUser(String id, String name, String email) { User user = new User(id, name, email); // Save user to database System.out.println("User created: " + user.getName());
// Notify subscribers eventBus.post(new UserCreatedEvent(user)); }
public void deleteUser(String userId) { // Delete user from database System.out.println("User deleted: " + userId);
// Notify subscribers eventBus.post(new UserDeletedEvent(userId)); } }
public static void main(String[] args) { EventBus eventBus = new EventBus();
EmailService emailService = new EmailService(); AuditService auditService = new AuditService(); UserService userService = new UserService(eventBus);
// Register services eventBus.register(emailService); eventBus.register(auditService);
// Create and delete users userService.createUser("1", "John Doe", "john@example.com"); userService.createUser("2", "Jane Smith", "jane@example.com"); userService.deleteUser("1");
eventBus.unregister(emailService); eventBus.unregister(auditService); }}
Best Practices
- Keep Events Simple: Events should be simple data carriers
- Use Descriptive Event Names: Make event names clear and descriptive
- Handle Exceptions: Implement proper error handling in event handlers
- Unregister Handlers: Always unregister handlers when done
- Use AsyncEventBus for Long Operations: Use async processing for time-consuming operations
- Monitor Event Flow: Use DeadEvent handlers to monitor unhandled events
Common Pitfalls
- Memory Leaks: Forgetting to unregister handlers
- Blocking Operations: Performing blocking operations in synchronous handlers
- Exception Propagation: Not handling exceptions in event handlers
- Event Ordering: Don’t rely on event processing order
- Circular Dependencies: Avoid posting events from within event handlers
Performance Considerations
- Synchronous vs Asynchronous: Use AsyncEventBus for long-running operations
- Handler Registration: Register/unregister handlers efficiently
- Event Object Creation: Create event objects efficiently
- Memory Usage: Monitor memory usage with many handlers
- Thread Safety: EventBus is thread-safe for posting events
Conclusion
Guava’s EventBus provides a simple and effective way to implement the publish-subscribe pattern in Java applications. It promotes loose coupling between components and enables event-driven architecture.
Use EventBus for decoupling components, implementing cross-cutting concerns, and building reactive systems. Remember to handle errors properly and unregister handlers when done.
EventBus is particularly useful for implementing features like logging, notifications, auditing, and any scenario where you need loose coupling between event producers and consumers.
Resources
Written by Purusothaman Ramanujam
← Back to blog