觀察者模式定義對象間一對多依賴,當一個對象(Subject/Observable)狀態改變,所有依賴它的對象(Observer)自動收到通知並更新。核心三要素:
update()。interface IObserver { void update(IObservable source); }
interface IObservable {
void register(IObserver o);
void unregister(IObserver o);
void notifyObservers();
String getState();
}
class Observable implements IObservable {
private final List<IObserver> observers = new CopyOnWriteArrayList<>(); // 線程安全
private String state;
public void register(IObserver o) { observers.add(o); }
public void unregister(IObserver o) { observers.remove(o); }
public void notifyObservers() {
for (IObserver o : observers) o.update(this);
}
// 狀態變更觸發通知
public void setState(String s) { this.state = s; notifyObservers(); }
}
class LoggerObserver implements IObserver {
public void update(IObservable src) {
System.out.println("Log: " + src.getState());
}
}
缺點:所有觀察者收到相同通知,無法區分事件類型;Subject 暴露全部狀態,違反最小知識原則。
interface IEvent { String getName(); String getState(); }
class EventA implements IEvent { ... }
class EventB implements IEvent { ... }
interface IObservable {
void register(IEvent event, IObserver observer);
void unregister(IEvent event, IObserver observer);
void notify(IEvent event);
}
class Observable implements IObservable {
private final Map<IEvent, Set<IObserver>> registry = new ConcurrentHashMap<>();
private final EventA eventA = new EventA();
private final EventB eventB = new EventB();
public void register(IEvent e, IObserver o) {
registry.computeIfAbsent(e, k -> ConcurrentHashMap.newKeySet()).add(o);
}
public void notify(IEvent e) {
Set<IObserver> set = registry.get(e);
if (set != null) set.forEach(o -> o.update(e));
}
// 觸發特定事件
public void changeA() { eventA.change(); notify(eventA); }
}
缺點:事件類型硬編碼在 Subject 中,擴展新事件需修改接口與實現,違反開閉原則。
class MyObservable extends Observable {
private String value;
public void setValue(String v) {
if (!Objects.equals(value, v)) {
value = v;
setChanged(); // 關鍵:標記已變更
notifyObservers(v); // 傳遞參數
}
}
}
class MyObserver implements Observer {
public void update(Observable o, Object arg) {
System.out.println("Received: " + arg);
}
}
缺點:Observable 是類而非接口,限制繼承;setChanged() 為 protected,外部不可調用;通知順序固定;Java 9 標記棄用。
Quartz 將監聽器按功能分類(SchedulerListener、JobListener、TriggerListener),每類獨立管理,接口定義細粒度回調:
// 核心調度器持有 List
public void addInternalSchedulerListener(SchedulerListener l) { ... }
public void notifySchedulerListenersScheduled(Trigger t) {
for (SchedulerListener l : getInternalSchedulerListeners()) l.jobScheduled(t);
}
優點:職責單一、擴展性強、線程安全、生命周期清晰。
引入事件總線作為中介者,徹底解耦發布者與訂閱者:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.0.0-jre</version>
</dependency>
// 1. 定義事件對象(POJO,不可變)
class UserRegisteredEvent {
final String userId;
final String email;
UserRegisteredEvent(String userId, String email) {
this.userId = userId; this.email = email;
}
}
// 2. 訂閱者:任意類,方法標註 @Subscribe
class EmailService {
@Subscribe
public void onUserRegistered(UserRegisteredEvent event) {
sendWelcomeEmail(event.email);
}
}
class AnalyticsService {
@Subscribe
public void onUserRegistered(UserRegisteredEvent event) {
track("user_registered", event.userId);
}
}
// 3. 發布者:注入 EventBus,post 事件
class UserService {
private final EventBus eventBus;
UserService(EventBus eventBus) { this.eventBus = eventBus; }
public void register(String userId, String email) {
// 保存用戶...
eventBus.post(new UserRegisteredEvent(userId, email)); // 同步發布
}
}
// 4. 組裝(Spring 配置示例)
@Configuration
class EventBusConfig {
@Bean EventBus eventBus() { return new EventBus(); } // 同步
// 異步: new AsyncEventBus(executor)
}
@Bean
EmailService emailService(EventBus bus) {
EmailService svc = new EmailService();
bus.register(svc);
return svc;
}
| 特性 | 說明 |
|---|---|
| 完全解耦 | 發布者不知訂閱者,訂閱者不知發布者,僅共享事件類 |
| 類型安全分發 | 基於參數類型自動路由,無需字符串常量 |
| 同步/異步靈活 | EventBus 同步,AsyncEventBus 異步(自定義 Executor) |
| 死碼檢測 | 編譯期發現未被訂閱的事件、未註冊的 @Subscribe 方法 |
| 異常隔離 | 單個訂閱者異常不影響其他訂閱者,可配置 SubscriberExceptionHandler |
| 零侵入 | POJO 事件、普通方法,無需實現接口、繼承基類 |
| 場景 | 推薦方案 |
|---|---|
| 簡單模塊內狀態同步 | 階段一:簡單接口 + CopyOnWriteArrayList |
| 模塊間多事件通知 | 階段二:事件類型 Map 分發 |
| 跨服務/跨模塊業務事件 | Guava EventBus (單體) / Spring ApplicationEvent (Spring 生態) / Kafka/RocketMQ (微服務) |
| 高性能異步事件處理 | Guava AsyncEventBus / Disruptor / Reactor Flux |
| 調度/框架擴展點 | Quartz 風格:分類 Listener 接口 + 優先級排序 |
觀察者模式演進路徑:硬編碼列表 → 事件類型分發 → 框架標準化 → 事件總線中介者。現代 Java 開發中,Guava EventBus 是單體應用內部事件驅動的首選,兼顧解耦、類型安全、同步異步靈活、零侵入。微服務架構下,領域事件應通過消息中間件(Kafka 等)跨服務傳遞,配合 EventBus 做本地發布,構建完整的事件驅動架構(EDA)。