觀察者模式 (發布-訂閱) 是解耦業務邏輯、實現事件驅動架構的核心模式。Java 提供 java.beans 內建支援,Spring 提供 ApplicationEvent,現代響應式編程用 RxJava/Project Reactor。本文從 JDK 原生 API 入手,逐層遞進至企業級事件總線與響應式流,附完整代碼對比。
PropertyChangeListener:監聽器介面,單方法 propertyChange(PropertyChangeEvent)PropertyChangeSupport:被觀察者委託類,管理監聽器註冊/移除/通知PropertyChangeEvent:事件對象,攜帶 propertyName、oldValue、newValuepublic class Domain {
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
private String name;
public String getName() { return name; }
public void setName(String name) {
String old = this.name;
this.name = name;
pcs.firePropertyChange("name", old, name); // 通知所有監聽器
}
public void addPropertyChangeListener(PropertyChangeListener l) { pcs.addPropertyChangeListener(l); }
public void removePropertyChangeListener(PropertyChangeListener l) { pcs.removePropertyChangeListener(l); }
}
public class NameLogger implements PropertyChangeListener {
@Override public void propertyChange(PropertyChangeEvent evt) {
if ("name".equals(evt.getPropertyName())) {
System.out.printf("名稱變更: %s -> %s%n", evt.getOldValue(), evt.getNewValue());
}
}
}
// 使用
Domain d = new Domain();
d.addPropertyChangeListener(new NameLogger());
d.setName("Alice"); // 控制台輸出: 名稱變更: null -> Alice
| 優點 | 缺點 |
|---|---|
| 零依賴、JDK 內建、線程安全 | 僅支援屬性變更事件、同步阻塞通知、無異步/背壓機制 |
public interface EventListener<T> { void onEvent(T event); }
public final class EventBus {
private final Map<Class<?>, List<EventListener<?>>> listeners = new ConcurrentHashMap<>();
private final ExecutorService asyncExecutor = Executors.newCachedThreadPool();
public <T> void subscribe(Class<T> eventType, EventListener<T> listener) {
listeners.computeIfAbsent(eventType, k -> new CopyOnWriteArrayList<>()).add(listener);
}
public <T> void publish(T event) {
List<EventListener<?>> list = listeners.get(event.getClass());
if (list != null) {
for (EventListener<?> l : list) {
// 同步調用
@SuppressWarnings("unchecked") EventListener<T> tl = (EventListener<T>) l;
tl.onEvent(event);
}
}
}
public <T> void publishAsync(T event) {
asyncExecutor.submit(() -> publish(event));
}
}
// 使用
class UserRegistered { String username; }
EventBus bus = new EventBus();
bus.subscribe(UserRegistered.class, e -> System.out.println("發送歡迎郵件: " + e.username));
bus.publishAsync(new UserRegistered("Alice"));
@Component
public class EmailService {
@EventListener // 自動訂閱
public void sendWelcome(UserRegisteredEvent event) {
emailSender.send(event.getEmail(), "歡迎註冊");
}
@EventListener(condition = "#event.vip") // SpEL 條件過濾
public void sendVipGift(UserRegisteredEvent event) { ... }
}
// 發布
@Autowired ApplicationEventPublisher publisher;
publisher.publishEvent(new UserRegisteredEvent("alice@test.com", true));
@Async 異步、@TransactionalEventListener 事務綁定 (提交後發布)| 特性 | JDK PropertyChange | 自定義 EventBus | Spring Events | RxJava / Reactor |
|---|---|---|---|---|
| 異步/非阻塞 | ❌ | ✅ (手動) | ✅ (@Async) | ✅ 原生 (Scheduler) |
| 背壓 | ❌ | ❌ | ❌ | ✅ (Flowable/Flux) |
| 操作符 (map/filter/flatMap) | ❌ | ❌ | ❌ | ✅ 豐富 |
| 錯誤處理/重試 | ❌ | 手動 | 有限 | ✅ 完善 |
| 適用場景 | 簡單 UI 綁定、Bean 屬性監聽 | 輕量級微服務內部解耦 | Spring 生態業務事件 | 高吞吐、流處理、複雜編排 |
PropertyChangeListenerEventBus (可引入 Guava EventBus / MBassador)ApplicationEventPublisher + @EventListener (首選)觀察者模式核心在於發布者與訂閱者解耦。Java 生態提供從同步阻塞到響應式非阻塞的完整譜系,根據併發量、延遲要求、技術棧、團隊熟悉度選擇最合適層級,避免過度設計。