Spring 的發布訂閱模式(Publish-Subscribe Pattern)是一種基于事件驅動的設計模式,通過 "事件" 作為中間載體實現組件間的解耦。在這種模式中,"發布者"(Publisher)負責產生事件并發布,"訂閱者"(Subscriber)通過訂閱特定事件接收通知并處理,兩者無需直接依賴,從而降低系統耦合度。
一、核心概念與角色
Spring 的發布訂閱模式主要涉及三個核心角色:
事件(Event)
事件是發布者與訂閱者之間的通信載體,封裝了需要傳遞的數據。在 Spring 中,所有事件都需繼承ApplicationEvent
(Spring 4.2 + 后可省略繼承,直接使用普通類作為事件)。發布者(Publisher)
負責創建并發布事件的組件。Spring 中通過ApplicationEventPublisher
接口(或其實現類,如ApplicationContext
)來發布事件,調用publishEvent()
方法即可。訂閱者(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
接口,因此可直接通過容器發布事件。實際開發中,通常通過依賴注入ApplicationEventPublisher
或ApplicationContext
來發布事件:
@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 事件處理是同步的:發布者發布事件后,會等待所有監聽器處理完成才繼續執行。若需異步處理(不阻塞發布者),可通過以下步驟實現:
- 啟用異步支持:在配置類上添加
@EnableAsync
注解。 - 標注異步方法:在監聽方法上添加
@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
的子類UserRegisteredEvent
和UserDeletedEvent
,監聽UserEvent
的監聽器會接收這兩個子類事件。
七、應用場景
Spring 發布訂閱模式適用于以下場景:
- 業務解耦:例如用戶注冊后,需要發送郵件、積分初始化、日志記錄等操作,通過事件分離這些邏輯,避免注冊服務與其他服務直接耦合。
- 異步通知:耗時操作(如短信發送、報表生成)通過異步事件處理,不阻塞主流程。
- 狀態變更通知:如訂單狀態變更后,通知庫存、支付、物流等相關模塊。
- 跨組件通信:不同模塊(如 Controller、Service、Repository)通過事件交互,無需直接依賴。
總結
Spring 的發布訂閱模式基于事件驅動,通過ApplicationEvent
、ApplicationListener
、ApplicationEventPublisher
三大組件實現,配合@EventListener
、@Async
、@TransactionalEventListener
等注解,提供了靈活、解耦的組件通信方式。其核心價值在于降低組件間耦合度,提高系統的可擴展性和可維護性。