1、監聽器的原理
ApplicationListener<T>是Spring框架中基于觀察者模式實現的事件監聽接口,用于監聽應用程序中特定類型的事件。該接口是一個函數式接口,從Spring 4.2開始支持Lambda表達式實現。
接口定義如下:
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {void onApplicationEvent(E event);
}
核心特性包括:
- ?泛型參數T?:限定監聽的事件類型,必須是ApplicationEvent的子類。
- ?函數式接口?:支持Lambda表達式簡化實現。
- ?事件過濾?:只會接收到指定類型的事件通知。
- ?觀察者模式?:實現發布-訂閱機制,解耦事件生產者和消費者?。
監聽器的原理:
- 事件發布者調用publishEvent()方法發布事件。
- ApplicationEventMulticaster獲取所有匹配的監聽器。
- 通過反射調用監聽器的onApplicationEvent()方法。
- 監聽器處理完成后返回,流程結束。
ApplicationEventMulticaster?是事件廣播器,負責管理監聽器和事件分發?。
下面是一段自定義監聽器的代碼,包括自定義事件、發布事件和監聽事件。
自定義事件CustomEvent.java:
package com.example.event;import org.springframework.context.ApplicationEvent;public class CustomEvent extends ApplicationEvent {private String data;public CustomEvent(Object source, String data) {super(source);this.data = data;}public String getData() {return data;}
}
EventPublisher類負責發布事件,真正觸發事件發布是在具體業務代碼中。
package com.example.event;import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;@Component
public class EventPublisher {private final ApplicationContext context;public EventPublisher(ApplicationContext context) {this.context = context;}public void publish(String data) {context.publishEvent(new CustomEvent(this, data));}
}
OrderService在業務處理完成后發布事件:
package com.example.service;import com.example.event.EventPublisher;
import org.springframework.stereotype.Service;@Service
public class OrderService {private final EventPublisher eventPublisher;public OrderService(EventPublisher eventPublisher) {this.eventPublisher = eventPublisher;}public void createOrder(String orderId) {// 業務處理...eventPublisher.publish("Order created: " + orderId);}
}
自定義監聽器OrderEventListener處理監聽事件:
package com.example.listener;import com.example.event.CustomEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;@Component
public class OrderEventListener implements ApplicationListener<CustomEvent> {@Overridepublic void onApplicationEvent(CustomEvent event) {System.out.println("收到訂單事件: " + event.getData());// 處理邏輯:發送通知、更新狀態等}
}
@EventListener 是Spring 4.2引入的基于注解的聲明式監聽方式?,實現與ApplicationListener接口相同的功能,并且更加靈活。建議優先使用@EventListener注解的方式實現監聽器,對于遺留系統需要保持與舊版本代碼風格一致?的可以使用ApplicationListener。
@Component
public class MyPreListener {@EventListenerpublic void handleRequestStart(ServletRequestEvent event) {System.out.println("請求開始之前進行預處理操作: " + event.getServletRequest());}@EventListenerpublic void handleRequestCompletion(ServletRequestHandledEvent event) {System.out.println("響應返回之前進行后置處理操作");}
}
2、監聽事件類型
Spring提供了豐富的事件類型,都是ApplicationEvent的子類,主要分為以下幾類:
1. 容器生命周期事件
- ?ContextRefreshedEvent?:容器刷新或初始化完成之后觸發。
- ?ContextStartedEvent?:容器調用start()方法啟動時觸發。
- ?ContextStoppedEvent?:容器調用stop()方法后觸發。
- ?ContextClosedEvent?:容器關閉時觸發?。
2. Spring Boot特有事件
- ?ApplicationStartingEvent?:應用啟動但未做任何初始化時觸發?。
- ?ApplicationEnvironmentPreparedEvent?:環境準備就緒但上下文未創建。
- ?ApplicationPreparedEvent?:容器刷新前觸發?。
- ?ApplicationReadyEvent?:應用完全就緒可接收請求。
- ?ApplicationFailedEvent?:啟動失敗時觸發。
3. Web相關事件
- ?ServletRequestEvent:請求開始時做一些預處理操作。
- ServletRequestHandledEvent?:請求結束返回響應之前做一些后置處理操作。?
- ?RequestHandledEvent?:請求處理完成后觸發(比ServletRequestHandledEvent更通用)。
- HttpSessionCreatedEvent:會話創建時觸發,用于會話管理。
- HttpSessionDestroyedEvent:會話銷毀時觸發,用于資源清理。
例如請求接口時可以通過?ServletRequestEvent事件做一些預處理操作,或者返回響應之前通過ServletRequestHandledEvent?事件做一些后置操作。
@Component
public class PreRequestListener implements ApplicationListener<ServletRequestEvent> {@Overridepublic void onApplicationEvent(ServletRequestEvent event) {// 請求預處理邏輯}
}@Component
public class PostRequestListener implements ApplicationListener<ServletRequestHandledEvent> {@Overridepublic void onApplicationEvent(ServletRequestHandledEvent event) {// 請求后處理邏輯}
}
下面重點看一下?ContextRefreshedEvent?和ApplicationReadyEvent事件。
根據Spring bean的初始化-CSDN博客這篇文章可知,常用的spring bean的初始化方法有以下四種:
(1)@PostConstruct注解的方法;
(2)類實現了InitializingBean接口,實現了afterPropertiesSet方法;
(3)通過XML配置文件在<bean>標簽中的init-method屬性指定初始化方法,或者@Bean的initMethod屬性指定的方法,如@Bean(initMethod = "init"),其中init是一個方法;
(4)還可以自定義后置處理器實現BeanPostProcessor接口,重寫postProcessBeforeInitialization方法實現初始化。
以上這四種初始化方法如果在一個類上同時使用,執行順序是(4)>(1)>(2)>(3)。
ContextRefreshedEvent?事件是在Spring容器完成初始化之后觸發的,它主要用于執行需要在所有Bean就緒后才能進行的全局操作,如緩存預熱、數據預加載等。?ContextRefreshedEvent?是Spring框架原生事件,表示Spring容器ApplicationContext已初始化或刷新完成?。在Spring Boot中,?ContextRefreshedEvent?位于ApplicationPreparedEvent之后和ApplicationStartedEvent之前,更在ApplicationReadyEvent之前?。
ApplicationReadyEvent具有與?ContextRefreshedEvent?相似的功能,也可以執行緩存預熱、數據預加載的操作。ApplicationReadyEvent是Spring Boot特有事件,表示應用程序已完全啟動并準備好接收請求?,是整個啟動過程中最后觸發的事件,確保所有基礎設施就緒?。ApplicationReadyEvent事件在ApplicationRunner與CommandLineRunner執行完成后?執行。
ContextRefreshedEvent?與ApplicationReadyEvent功能特性對比:
特性 | ContextRefreshedEvent | ApplicationReadyEvent |
所屬框架 | Spring原生 | Spring Boot特有 |
觸發確定性 | 可能多次觸發(父子容器) | 確保只觸發一次 |
基礎設施狀態 | 容器就緒,外部服務未保證 | 所有基礎設施(如Web服務器)已就緒 |
適用階段 | 容器初始化后 | 應用完全可用時 |
健康檢查 | 未執行 | 已通過? |
應用場景分析:
1. ContextRefreshedEvent適用場景
- ?Bean初始化后的配置調整?:當需要基于已初始化的Bean進行動態配置時。
- ?內部緩存預熱?:加載不依賴外部服務的內部緩存數據。
- ?早期資源初始化?:需要在容器就緒后立即執行的輕量級操作。
- ?多模塊系統中的模塊間協調?:在復雜應用中協調不同模塊的初始化順序。
2. ApplicationReadyEvent適用場景
- ?外部服務連接?:建立與數據庫、消息隊列等外部服務的連接。
- ?定時任務啟動?:確保所有依賴Bean就緒后再啟動定時任務。
- ?服務注冊?:向服務注冊中心(如Eureka)注冊服務實例。
- ?就緒狀態通知?:通知監控系統應用已準備好接收流量。
- ?關鍵資源檢查?:驗證所有必要服務是否正常啟動。
實際工作中的選擇建議:
純Spring應用?:只能使用ContextRefreshedEvent?。
Spring Boot應用?:
- 需要確保基礎設施就緒 → 選擇ApplicationReadyEvent?。
- 僅需容器初始化后操作 → 選擇ContextRefreshedEvent?。
- 微服務架構中的服務注冊 → 必須使用ApplicationReadyEvent?。
ContextRefreshedEvent中應檢查event.getApplicationContext().getParent()避免重復執行?,ApplicationReadyEvent天然保證單次執行?。ContextRefreshedEvent中的異常可能阻止應用啟動?,ApplicationReadyEvent中的異常通常不會阻止啟動但應記錄?。在springboot項目中,如果需要在Spring容器初始化之后,且正式對外提供服務之前做一些預處理操作,盡量使用ApplicationReadyEvent?。
ContextRefreshedEvent事件監聽的代碼示例:
@Component
public class MyContextListener implements ApplicationListener<ContextRefreshedEvent> {@Overridepublic void onApplicationEvent(ContextRefreshedEvent event) {// 避免在Web環境中因父子容器重復執行if (event.getApplicationContext().getParent() == null) {// 執行容器初始化后的操作System.out.println("Context refreshed, beans are ready");}}
}
ApplicationReadyEvent事件監聽的代碼示例:
@Component
public class MyAppReadyListener {@EventListenerpublic void onApplicationReady(ApplicationReadyEvent event) {// 應用完全就緒后可安全執行的操作System.out.println("Application is fully ready to serve requests");// 示例:啟動定時任務ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);scheduler.scheduleAtFixedRate(() -> System.out.println("Periodic task running"), 0, 1, TimeUnit.MINUTES);}
}
通過過濾器和攔截器實現請求前和請求后進行處理操作:
Filter實現請求前后處理:
@Component
public class MyFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {System.out.println("請求前處理");chain.doFilter(request, response);System.out.println("請求后處理");}
}
Interceptor實現請求前后處理:
@Component
public class MyInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("請求前處理");return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("請求后處理");}
}