Using Guava EventBus for Publish-Subscribe Communication in Java
Overview
Guava's EventBus provides an elegant publish-subscribe communication mechanism for components within a single service. It serves as a clean implementation of the Observer pattern.
The key distinction between traditional Observer pattern and EventBus lies in the implementation approach. In the Observer pattern, publishers must extend Observable and implement event publishing logic, while listeners need to implement the Observer interface. With EventBus, you simply post events to the bus and register listeners, with listener methods annotated using @Subscribe.
Implementation
This example demonstrates a notification system where a publisher broadcasts messages to registered subscribers.
Defining Events
The publisher can emit events of any object type or primitive values:
public class NotificationEvent {
private String content;
private long timestamp;
public NotificationEvent(String content) {
this.content = content;
this.timestamp = System.currentTimeMillis();
}
public String getContent() {
return content;
}
public long getTimestamp() {
return timestamp;
}
}
Creating Subscribers
After integrating Guava, the publish-subscribe pattern becomes straightforward. To subscrieb to a specific message type, simply annotate the handler method with @Subscribe.
Key behaviors:
- A single subscriber can listen to multiple event types. Guava determines which method to invoke based on the event type and the parameter type of the subscribed method
- When multiple subscribers listen to the same event, each receives the notification in the order they were registered
import com.google.common.eventbus.Subscribe;
public class AlertReceiver {
@Subscribe
public void handleNotification(NotificationEvent event) {
System.out.println("AlertReceiver got: " + event.getContent());
}
@Subscribe
public void handleTextMessage(String message) {
System.out.println("AlertReceiver got text: " + message);
}
}
import com.google.common.eventbus.Subscribe;
public class LoggerSubscriber {
@Subscribe
public void handleNotification(NotificationEvent event) {
System.out.println("LoggerSubscriber logged: " + event.getContent());
}
@Subscribe
public void handleTextMessage(String message) {
System.out.println("LoggerSubscriber logged text: " + message);
}
@Subscribe
public void handleNumericAlert(Integer alertCode) {
System.out.println("LoggerSubscriber logged alert code: " + alertCode);
}
}
Event Registration and Publishing
public class EventBusExample {
public static void main(String[] args) {
// Initialize the event bus with an identifier
EventBus notificationBus = new EventBus("notifications");
// Register all subscribers
notificationBus.register(new AlertReceiver());
notificationBus.register(new LoggerSubscriber());
// Publish notification events
notificationBus.post(new NotificationEvent("System startup complete"));
notificationBus.post(new NotificationEvent("Configuration reloaded"));
// Publish plain text messages
notificationBus.post("User login detected");
notificationBus.post("Cache cleared");
// Publish numeric data
notificationBus.post(200);
notificationBus.post(404);
}
}
Execution Flow
Both AlertReceiver and LoggerSubscriber are registered for NotificationEvent, so both receive those events. AlertReceiver processes events first since it was registered earlier.
LoggerSubscriber handles integer alerts, while AlertReceiver does not since it never registered for that type.
Sample Output
AlertReceiver got: System startup complete
LoggerSubscriber logged: System startup complete
AlertReceiver got: Configuration reloaded
LoggerSubscriber logged: Configuration reloaded
AlertReceiver got text: User login detected
LoggerSubscriber logged text: User login detected
AlertReceiver got text: Cache cleared
LoggerSubscriber logged text: Cache cleared
LoggerSubscriber logged alert code: 200
LoggerSubscriber logged alert code: 404