Spring 的 ApplicationEvent 機制實現了觀察者模式,支撐框架內部(Context 生命週期)與業務層(解耦通知)的異步解耦通信。本文系統梳理核心接口、自定義事件實戰、內建 Context 事件、源碼級發布流程、線程模型與線程池配置、多監聽器排序等全鏈路知識。
| 類型 | 名稱 | 職責 |
|---|---|---|
| 接口 | ApplicationEventPublisherAware | Bean 實現後自動注入 ApplicationEventPublisher,獲得發布事件能力 |
| 抽象類 | ApplicationEvent | 事件載體,封裝 source(事件源)與 timestamp(發生時間) |
| 接口 | ApplicationListener<E extends ApplicationEvent> | 監聽器契約,泛型指定關注事件類型,onApplicationEvent(E) 處理邏輯 |
public class MealEvent extends ApplicationEvent {
private final MealEnum mealEnum;
public MealEvent(String content, MealEnum mealEnum) {
super(content); // source = content
this.mealEnum = mealEnum;
}
public MealEnum getMealEnum() { return mealEnum; }
}
public enum MealEnum { breakfast, lunch, dinner }
@Component
public class CustomizePublisher implements ApplicationEventPublisherAware {
private ApplicationEventPublisher publisher;
public void publish(MealEvent event) { publisher.publishEvent(event); }
@Override public void setApplicationEventPublisher(ApplicationEventPublisher p) { this.publisher = p; }
}
@Component
public class MealListener implements ApplicationListener<MealEvent> {
@Override public void onApplicationEvent(MealEvent event) {
System.out.printf("Thread:%s, Type:%s, Event:%s%n",
Thread.currentThread().getName(), event.getMealEnum(), event);
// 業務分發...
}
}
// 捕獲所有事件(慎用,影響性能)
@Component
public class AllAcceptedListener implements ApplicationListener<ApplicationEvent> { ... }
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config.xml");
CustomizePublisher publisher = ctx.getBean(CustomizePublisher.class);
publisher.publish(new MealEvent("A吃午飯", MealEnum.lunch));
// 輸出: Thread:main, Type:lunch, Event:MealEvent[source=A吃午飯]
MealListener 只收 MealEvent,AllAcceptedListener 收所有)。onApplicationEvent,異常會阻塞發布者。| 事件 | 觸發時機 | 典型用途 |
|---|---|---|
ContextRefreshedEvent | 容器初始化/刷新完成 (finishRefresh()) | 啟動後初始化緩存、註冊定時任務、啟動嵌入式服務 |
ContextClosedEvent | 容器關閉 (doClose()) | 優雅關閉線程池、釋放資源、發送下線通知 |
ContextStartedEvent | context.start() | 啟動 Lifecycle Bean(如消息消費者) |
ContextStoppedEvent | context.stop() | 停止 Lifecycle Bean |
publishEvent(Object event) → 包裝為 ApplicationEvent(若非子類)。getApplicationEventMulticaster().multicastEvent(event)。SimpleApplicationEventMulticaster.multicastEvent:
getApplicationListeners(event, type)。invokeListener(listener, event)。invokeListener 默認同步調用 listener.onApplicationEvent(event)。默認:發布線程 = 處理線程(同步)。
異步化方案:自定義 ApplicationEventMulticaster Bean,注入 TaskExecutor:
@Bean
public ApplicationEventMulticaster applicationEventMulticaster(ThreadPoolTaskExecutor executor) {
SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
multicaster.setTaskExecutor(executor); // 異步執行監聽器
return multicaster;
}
@Bean
public ThreadPoolTaskExecutor eventExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(50);
executor.setQueueCapacity(200);
executor.setThreadNamePrefix("event-");
executor.initialize();
return executor;
}
異步化後,發布者立即返回,監聽器在線程池中並行執行。需注意:異常不再傳播給發布者,需在監聽器內部捕獲處理或配置 ErrorHandler。
實現 Ordered 接口或標註 @Order,value 越小越優先:
@Component
@Order(1) // 優先級最高
public class FirstListener implements ApplicationListener<MyEvent> { ... }
@Component
@Order(2)
public class SecondListener implements ApplicationListener<MyEvent> { ... }
final,構造器賦值,避免多線程可見性問題。@EventListener 註解替代 ApplicationListener,配合 @Async 更簡潔。