在企業級應用開發中,業務模塊間常存在「一處觸發、多方響應」的鬆耦合需求。觀察者模式是解決此類問題的經典方案,而 Spring 容器的依賴注入(DI)與 Bean 管理機制,能將觀察者模式的靈活性提升到新高度。本文基於真實專案經驗,展示如何在 Spring 中優雅落地觀察者模式,實現業務模塊零代碼修改的動態訂閱與發布。
系統中存在功能點 A,其業務操作需觸發 B、C、D 等相關功能點進行數據同步處理。但客戶採購的功能模塊各不相同:有的客戶只有 B、D,缺少 C。傳統硬編碼調用會導致:
觀察者模式天然契合:Subject(被觀察者) 維護 Observer(觀察者) 列表,狀態變更時統一通知。但傳統實現中 Observer 列表往往硬編碼在代碼或配置文件中,變更仍需重啟或改代碼。
public abstract class StaffingObserver {
/**
* 統一更新入口,參數攜帶業務上下文
* @param para 業務參數 Map,靈活擴展
*/
public abstract void update(Map para) throws BaseException;
}
public class TerminateDispatch extends StaffingObserver {
private DispatchDAO dispatchDAO; // Spring 注入依賴
@Override
public void update(Map para) throws BaseException {
// 具體業務邏輯:根據 para 參數執行終止派遣相關操作
// dispatchDAO.xxx(...)
}
public void setDispatchDAO(DispatchDAO dispatchDAO) {
this.dispatchDAO = dispatchDAO;
}
}
關鍵點:觀察者作為普通 Spring Bean,享受完整 DI 能力,可注入 DAO、Service 等依賴,業務邏輯完整保留。
public class StaffingObservable {
private List allConfig; // 觀察者 Bean 名稱列表,由 Spring 注入
/**
* 遍歷配置列表,從 Spring 容器獲取 Observer 實例並通知
*/
public void notifyForList(Map para) throws BaseException {
Iterator it = allConfig.iterator();
while (it.hasNext()) {
String beanName = it.next();
StaffingObserver observer = (StaffingObserver) getBean(beanName);
if (observer != null) {
observer.update(para);
} else {
log.warn("Observer bean '{}' does not exist.", beanName);
}
}
}
private Object getBean(String beanName) {
return TbApplicationContext.getWac().getBean(beanName); // 獲取 WebApplicationContext
}
public void setAllConfig(List allConfig) {
this.allConfig = allConfig;
}
}
TbApplicationContext 為項目通用的 Spring 容器持有工具類,實現 ApplicationContextAware 靜態獲取上下文。
步驟一:定義具體觀察者 Bean
```xml步驟二:在客戶端配置中裝配 Observable 並注入觀察者列表
```xml| 維度 | 傳統觀察者模式 | Spring 化觀察者模式 |
|---|---|---|
| 觀察者註冊 | 代碼硬編碼 subject.addObserver() | Spring XML/註解配置,熱插拔 |
| 依賴管理 | 觀察者自行查找/構建依賴 | 容器統一 DI,生命週期托管 |
| 客戶差異化 | 多版本代碼/配置文件 | 同一代碼庫,不同 XML 裝配組合 |
| 新增響應方 | 修改 Subject 代碼重編譯部署 | 僅需新增 Bean 定義 + XML 添加一行 |
| 調試難度 | 調用棧清晰 | 間接調用,需良好命名與文檔輔助 |
[BusinessAction][Domain]Observer,如 TerminateDispatchObserver,配置列表可讀性強。notifyForList 中單個 Observer 異常不應阻斷後續通知,建議 try-catch 包裹並記錄錯誤日誌。@Async 或 ApplicationEventPublisher 實現非阻塞通知,提升主流程響應速度。@Component + @Autowired List<StaffingObserver> observers 自動收集所有實現類,進一步簡化配置。Spring 內建 ApplicationEvent + ApplicationListener 實現了標準觀察者模式,更輕量:
// 事件定義
public class StaffingEvent extends ApplicationEvent {
private Map data;
public StaffingEvent(Object source, Map data) { super(source); this.data = data; }
public Map getData() { return data; }
}
// 發布者
@Service
public class StaffingService {
@Autowired ApplicationEventPublisher publisher;
public void doBusiness() {
// ...
publisher.publishEvent(new StaffingEvent(this, params));
}
}
// 監聽者(自動註冊)
@Component
public class TerminateDispatchListener implements ApplicationListener {
@Override public void onApplicationEvent(StaffingEvent event) {
// 處理邏輯
}
}
優勢:零配置自動發現、支援異步 @EventListener、有序 @Order、泛型事件類型安全。建議新專案優先考慮 Spring Event,舊專案遷移時可共存。