Spring發布訂閱模式詳解

Spring 的發布訂閱模式(Publish-Subscribe Pattern)是一種基于事件驅動的設計模式,通過 "事件" 作為中間載體實現組件間的解耦。在這種模式中,"發布者"(Publisher)負責產生事件并發布,"訂閱者"(Subscriber)通過訂閱特定事件接收通知并處理,兩者無需直接依賴,從而降低系統耦合度。

一、核心概念與角色

Spring 的發布訂閱模式主要涉及三個核心角色:

  1. 事件(Event)
    事件是發布者與訂閱者之間的通信載體,封裝了需要傳遞的數據。在 Spring 中,所有事件都需繼承ApplicationEvent(Spring 4.2 + 后可省略繼承,直接使用普通類作為事件)。

  2. 發布者(Publisher)
    負責創建并發布事件的組件。Spring 中通過ApplicationEventPublisher接口(或其實現類,如ApplicationContext)來發布事件,調用publishEvent()方法即可。

  3. 訂閱者(Subscriber)
    負責監聽并處理特定事件的組件。Spring 中訂閱者可通過實現ApplicationListener接口,或使用@EventListener注解定義事件處理方法。

二、Spring 事件機制的核心組件

1. ApplicationEvent(事件基類)

ApplicationEvent是 Spring 事件的基類,繼承自 JDK 的EventObject,包含事件源(source)和事件發生時間(timestamp)。

// Spring內置的ApplicationEvent
public abstract class ApplicationEvent extends EventObject {private final long timestamp; // 事件發生時間public ApplicationEvent(Object source) {super(source);this.timestamp = System.currentTimeMillis();}public final long getTimestamp() {return this.timestamp;}
}

自定義事件示例
通常通過繼承ApplicationEvent定義業務事件:

// 自定義用戶注冊事件
public class UserRegisteredEvent extends ApplicationEvent {private User user; // 事件中攜帶的用戶數據public UserRegisteredEvent(Object source, User user) {super(source);this.user = user;}public User getUser() {return user;}
}

Spring 4.2 + 后支持非繼承 ApplicationEvent 的事件,直接使用普通類即可:

// 無需繼承ApplicationEvent的事件
public class OrderCreatedEvent {private Order order;// 構造器、getter等
}
2. ApplicationListener(訂閱者接口)

ApplicationListener是訂閱者的核心接口,用于定義事件處理邏輯,泛型參數指定需要監聽的事件類型。

// Spring的事件監聽器接口
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {// 事件處理方法,當監聽的事件被發布時調用void onApplicationEvent(E event);
}

實現接口的訂閱者示例

// 監聽UserRegisteredEvent的訂閱者(發送歡迎郵件)
@Component
public class WelcomeEmailListener implements ApplicationListener<UserRegisteredEvent> {@Overridepublic void onApplicationEvent(UserRegisteredEvent event) {User user = event.getUser();System.out.println("給用戶" + user.getName() + "發送歡迎郵件...");}
}
3. ApplicationEventPublisher(發布者接口)

ApplicationEventPublisher是發布事件的接口,定義了發布事件的方法:

public interface ApplicationEventPublisher {// 發布事件void publishEvent(ApplicationEvent event);// Spring 4.2+新增,支持發布非ApplicationEvent類型的事件void publishEvent(Object event);
}

發布者的實現
Spring 的ApplicationContext(容器本身)實現了ApplicationEventPublisher接口,因此可直接通過容器發布事件。實際開發中,通常通過依賴注入ApplicationEventPublisherApplicationContext來發布事件:

@Service
public class UserService {// 注入事件發布器@Autowiredprivate ApplicationEventPublisher publisher;public void register(User user) {// 1. 執行注冊邏輯System.out.println("用戶" + user.getName() + "注冊成功");// 2. 發布用戶注冊事件publisher.publishEvent(new UserRegisteredEvent(this, user));}
}

三、注解驅動的事件監聽(@EventListener)

Spring 4.2 引入@EventListener注解,無需實現ApplicationListener接口,直接在方法上標注即可定義事件處理邏輯,更簡潔靈活。

基本用法
@Component
public class UserEventHandler {// 監聽UserRegisteredEvent事件@EventListenerpublic void handleUserRegisteredEvent(UserRegisteredEvent event) {User user = event.getUser();System.out.println("處理用戶注冊事件:" + user.getName());}// 監聽多個事件(方法參數為多個事件類型)@EventListenerpublic void handleMultiEvents(UserRegisteredEvent userEvent, OrderCreatedEvent orderEvent) {// 處理邏輯}
}
條件監聽(condition)

通過condition屬性指定 SpEL 表達式,滿足條件時才執行監聽邏輯:

@EventListener(condition = "#event.user.age > 18") // 只處理成年用戶的注冊事件
public void handleAdultUserRegistered(UserRegisteredEvent event) {// 處理邏輯
}
事件順序(@Order)

多個監聽器監聽同一事件時,通過@Order指定執行順序(值越小越先執行)

@Order(1) // 先執行
@EventListener
public void handleFirst(UserRegisteredEvent event) { ... }@Order(2) // 后執行
@EventListener
public void handleSecond(UserRegisteredEvent event) { ... }

四、異步事件處理

默認情況下,Spring 事件處理是同步的:發布者發布事件后,會等待所有監聽器處理完成才繼續執行。若需異步處理(不阻塞發布者),可通過以下步驟實現:

  1. 啟用異步支持:在配置類上添加@EnableAsync注解。
  2. 標注異步方法:在監聽方法上添加@Async注解。

示例:

// 1. 配置類啟用異步
@Configuration
@EnableAsync
public class AsyncConfig {// 可選:自定義線程池@Beanpublic Executor taskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(5);executor.setMaxPoolSize(10);executor.setQueueCapacity(25);executor.initialize();return executor;}
}// 2. 異步處理事件的監聽器
@Component
public class AsyncUserListener {@Async // 異步執行@EventListenerpublic void handleAsync(UserRegisteredEvent event) {System.out.println("異步處理事件:" + Thread.currentThread().getName());// 耗時操作(如發送短信、調用第三方接口等)}
}

五、事務綁定事件(@TransactionalEventListener)

在業務中,常需要在事務完成后(提交 / 回滾)再處理事件(例如:訂單事務提交后再發送通知)。Spring 提供@TransactionalEventListener注解,支持綁定事務生命周期。

注解的phase屬性指定事務階段:

  • AFTER_COMMIT:事務提交后(默認)
  • AFTER_ROLLBACK:事務回滾后
  • AFTER_COMPLETION:事務完成后(無論提交還是回滾)
  • BEFORE_COMMIT:事務提交前

示例:

@Service
public class OrderService {@Autowiredprivate ApplicationEventPublisher publisher;@Transactionalpublic void createOrder(Order order) {// 保存訂單(事務內操作)orderRepository.save(order);// 發布事件(實際處理會在事務提交后)publisher.publishEvent(new OrderCreatedEvent(order));}
}@Component
public class OrderEventListener {// 訂單事務提交后才處理事件@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)public void handleOrderCreated(OrderCreatedEvent event) {System.out.println("訂單" + event.getOrder().getId() + "已提交,發送通知...");}
}

六、事件傳播機制

Spring 事件具有層次性:監聽器可監聽父類事件,從而接收所有子類事件。例如:

  • ApplicationEvent是所有事件的父類,監聽ApplicationEvent的監聽器會接收所有類型的事件。
  • 自定義事件UserEvent的子類UserRegisteredEventUserDeletedEvent,監聽UserEvent的監聽器會接收這兩個子類事件。

七、應用場景

Spring 發布訂閱模式適用于以下場景:

  1. 業務解耦:例如用戶注冊后,需要發送郵件、積分初始化、日志記錄等操作,通過事件分離這些邏輯,避免注冊服務與其他服務直接耦合。
  2. 異步通知:耗時操作(如短信發送、報表生成)通過異步事件處理,不阻塞主流程。
  3. 狀態變更通知:如訂單狀態變更后,通知庫存、支付、物流等相關模塊。
  4. 跨組件通信:不同模塊(如 Controller、Service、Repository)通過事件交互,無需直接依賴。

總結

Spring 的發布訂閱模式基于事件驅動,通過ApplicationEventApplicationListenerApplicationEventPublisher三大組件實現,配合@EventListener@Async@TransactionalEventListener等注解,提供了靈活、解耦的組件通信方式。其核心價值在于降低組件間耦合度,提高系統的可擴展性和可維護性。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/94116.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/94116.shtml
英文地址,請注明出處:http://en.pswp.cn/web/94116.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

服務器硬件中的磁盤SSD與HDD性能區別,以及分別適用于什么業務?

SSD&#xff08;固態硬盤&#xff09;和 HDD&#xff08;機械硬盤&#xff09;是服務器中常見的存儲設備類型&#xff0c;兩者在性能、可靠性、成本等方面存在顯著差異。根據這些特性&#xff0c;它們適用于不同的業務需求。以下是詳細的對比與應用場景分析&#xff1a;1. SSD …

AI驅動的SEO關鍵詞優化秘籍

內容概要人工智能技術的飛速發展正重塑SEO關鍵詞優化領域&#xff0c;為從業者帶來全新機遇與挑戰。本文將系統解析AI如何革新關鍵詞策略&#xff0c;覆蓋從語義搜索深度解析到長尾詞智能挖掘的核心環節。通過工具驅動的內容優化路徑&#xff0c;讀者將掌握提升流量轉化率的關鍵…

自然語言處理(NLP)技術的發展歷史

自然語言處理&#xff08;NLP&#xff09;作為人工智能的重要分支&#xff0c;其發展歷程跨越了大半個世紀&#xff0c;從早期的規則式嘗試到如今的大模型時代&#xff0c;技術路徑不斷迭代&#xff0c;核心目標始終是實現人機間的自然語言交互。以下從關鍵階段、技術突破和標志…

Swift 解法詳解 LeetCode 361:轟炸敵人,用動態規劃輕松拿下

文章目錄摘要描述題解答案題解代碼分析代碼解析示例測試及結果時間復雜度空間復雜度總結摘要 “轟炸敵人”這道題名字聽起來就很帶感&#xff0c;它其實是一個二維網格搜索問題。我們要找到一個能放置炸彈的位置&#xff0c;讓炸掉的敵人最多。雖然題目看起來復雜&#xff0c;…

如何高效推進將科技創新成果轉化為標準?

2024年10月26日&#xff0c;全國標準信息公共服務平臺正式發布了國家標準《科技成果評估規范》&#xff08;GB/T 44731-2024 &#xff09;&#xff0c;并從發布之日起正式實施。這一標準的正式推出&#xff0c;標志著政府在推進科技成果轉化、提升科技服務能力方面邁出了重要一…

CMake 快速開始

CMake 快速開始 CMake 安裝 編輯環境&#xff1a;VS Code 編譯環境&#xff1a;VS Code Remote SSH模式 Ubuntu 24.04 CMake 官?源代碼下載地址&#xff1a;https://cmake.org/download/ CMake 官?英? 檔地址&#xff1a;https://cmake.org/cmake/help/latest/index.html S…

STM32F1 EXTI介紹及應用

第三章 EXTI介紹及應用 1. EXTI介紹 EXTI&#xff08;External interrupt/event controller&#xff09;—外部中斷/事件控制器&#xff0c;管理了控制器的 20 個中斷/事件線。每個中斷/事件線都對應有一個邊沿檢測器&#xff0c;可以實現輸入信號的上升沿檢測和下降沿的檢測。…

Oracle SYS用戶無法登錄數據庫-ORA-12162

錯誤詳情 [Oracleorcl bin]$ ./sqlplus / as sysdba SQL*Plus: Release 11.2.0.4.0 Production on Mon Aug 18 08:12:04 2025 Copyright (c) 1982, 2013, Oracle. All rights reserved. ERROR: ORA-12162: TNS:net service name is incorrectly specifiedOS登錄解析 注意&…

【計算機視覺與深度學習實戰】06基于光流算法的實時運動檢測系統設計與實現——以蚊子軌跡追蹤為例(有完整代碼)

第一章 引言 計算機視覺作為人工智能領域的重要分支,近年來在目標檢測、運動分析、行為識別等方面取得了顯著進展。其中,運動檢測技術作為視頻分析的基礎技術之一,在安防監控、交通管理、體感交互、生物行為研究等領域發揮著越來越重要的作用。光流算法作為運動檢測的經典方…

國產CANFD芯片技術特性與應用前景綜述:以ASM1042系列為例

摘要本文綜述了國科安芯推出的國產CANFD芯片ASM1042系列的技術特性與應用前景。ASM1042系列作為一款高性能的CANFD收發器&#xff0c;支持5Mbps的高速通信和高達70V的總線耐壓&#xff0c;廣泛應用于汽車電子、工業控制和航空航天等領域。文中詳細分析了其高速率設計、高耐壓設…

偶現型Bug處理方法---用系統方法對抗隨機性

在軟件開發中&#xff0c;Bug是影響產品質量的核心問題&#xff0c;而偶現型Bug&#xff08;Intermittent Bug&#xff09;因其“時隱時現、難以復現”的特性&#xff0c;成為最頭疼的挑戰之一。這類Bug不像必現Bug那樣有穩定的觸發路徑&#xff0c;可能在特定環境、特定操作序…

一分鐘docker部署onlyoffice 在線預覽word pdf excel...

目錄 效果 1.執行命令 2.訪問 3.測試 3.1執行下面的命令 3.2測試效果 3.3預覽效果 3.4轉換 效果 1.執行命令 sudo docker run -i -t -d -p 80:80 onlyoffice/documentserver 稍等片刻 2.訪問 瀏覽器打開ip:80即可訪問 3.測試 3.1執行下面的命令 sudo docker exec 7…

ES_數據存儲知識

一、 _source 字段&#xff1a;數據的“真相之源” 1. 是什么&#xff1f; _source 是一個獨立的、特殊的元字段。它存儲了你在索引文檔時提交的原始JSONbody的完整內容。 2. 工作原理與用途 寫入&#xff1a;當你索引一個文檔 {"title": "My Book", "…

day37-Nginx優化

1.每日復盤與今日內容1.1復盤nginx四層轉發rewrite tag&#xff1a;last和breakredirect、permanent&#x1f35f;&#x1f35f;&#x1f35f;&#x1f35f;&#x1f35f;Nginx內置參數動靜分離&#x1f35f;&#x1f35f;&#x1f35f;&#x1f35f;&#x1f35f;1.2今日內容N…

Zynq開發實踐(fpga高頻使用的兩個場景)

【 聲明&#xff1a;版權所有&#xff0c;歡迎轉載&#xff0c;請勿用于商業用途。 聯系信箱&#xff1a;feixiaoxing 163.com】本身fpga是介于純軟件和asic之間的元器件。如果是純軟件&#xff0c;那我們要做的&#xff0c;就是純上層開發。只要相關驅動已經實現&#xff0c;那…

20250822在Ubuntu24.04.2下指定以太網卡的IP地址

20250822在Ubuntu24.04.2下指定以太網卡的IP地址 2025/8/22 20:28緣起&#xff1a;公司的服務器的IP地址老變&#xff01;&#xff0c;路由器經常被其他其它部門斷電重啟。 導致IP地址被DHCP服務器給更改了&#xff01; 直接固定IP地址了。 本來想通過VI命令編輯配置文件來指定…

【yocto】BitBake指令匯總解析

【點關注&#xff0c;不迷路 】BitBake 是一個功能強大且核心的元任務執行器&#xff0c;它是 OpenEmbedded 和 Yocto Project 的構建基石。簡單來說&#xff0c;它就像一個高度專業化的 make 工具&#xff0c;但它能解析復雜的元數據&#xff08;配方、配置、類&#xff09;&…

CSS @media 媒體查詢

media 媒體查詢是響應式設計的核心工具&#xff0c;允許根據設備特性&#xff08;如屏幕寬度、高度、方向等&#xff09;應用不同的 CSS 樣式。一、基本語法media media-type and (media-feature) {/* 目標樣式規則 */ }媒體類型&#xff08;可選&#xff09;&#xff1a;all&a…

Vue2.x核心技術與實戰(三)

目錄 四、Vue2.x:組件通信&進階用法 4.1 組件的三大組成部分(結構/樣式/邏輯) 4.1.0 組件的三大組成部分-注意點說明 4.1.1 組件的樣式沖突 scoped 4.1.2 data是一個函數 4.2 組件通信 4.2.1 什么是組件通信 4.2.2 不同的組件關系和組件通信方案分類 4.2.2 父傳子…

泵站遠程監控與自動化控制系統:智慧泵房設備的創新實踐

在智慧水務快速發展的背景下&#xff0c;泵站自動化控制系統與水泵遠程監控技術已成為提升供水效率、保障水質安全、降低運維成本的核心手段。通過物聯網、云計算、邊緣計算等技術的深度融合&#xff0c;智慧泵房設備實現了從“人工值守”到“無人化智能管理”的跨越式升級&…