你好,我是劉牌!
好久沒有更新Spring了,今天來分享一下Spring的事件監聽機制,之前分享過一篇Spring監聽機制的使用,今天從原理上進行解析,Spring的監聽機制基於觀察者模式,就是就是我們所說的釋出訂閱模式,這種模式可以在一定程度上實現程式碼的解耦,如果想要實現系統層面的解耦,那麼訊息佇列就是我們的不二選擇,訊息佇列本身也是釋出訂閱模式,只是不同的訊息佇列的實現方式不一樣。
之前的文章我們使用了註解的方式,今天我們使用介面的方式來實現。
如下定義了一個事件AppEvent,它繼承了ApplicationEvent類,如果我們要使用Spring的事件監聽機制,那麼我們定義的事件必須繼承ApplicationEvent ,否則就無法使用。
/**
* 功能說明: 事件
* <p>
* Original @Author: steakliu-劉牌, 2023-03-30 11:02
* <p>
* Copyright (C)2020-2022 steakliu All rights reserved.
*/
public class AppEvent extends ApplicationEvent {
private final String event;
public AppEvent(Object source, String event) {
super(source);
this.event = event;
}
public String getEvent() {
return event;
}
}
事件監聽器實現了ApplicationLister介面,其泛型為ApplicationEvent,因為要監聽事件,所以必須按照Spring的規則來,onApplicationEvent方法就是監聽到的事件,在這裡我們可以進行我們的業務處理,我們可以看出AppLister我們加上了@Component註解,因為事件監聽器需要加入Spring IOC容器中才能生效。
/**
* 功能說明:事件監聽器
* <p>
* Original @Author: steakliu-劉牌, 2023-03-30 11:03
* <p>
* Copyright (C)2020-2022 steakliu All rights reserved.
*/
@Component
public class AppListener implements ApplicationListener<AppEvent> {
@Override
public void onApplicationEvent(AppEvent event) {
System.out.println("event: "+event.getEvent());
}
}
有了事件監聽器,就需要釋出事件,所以就需要一個事件釋出器,事件釋出器使用的是ApplicationEventPublisher,使用它的publishEvent方法進行事件釋出。
/**
* 功能說明:事件釋出器
* <p>
* Original @Author: steakliu-劉牌, 2023-06-11 13:55
* <p>
* Copyright (C)2020-2022 steakliu All rights reserved.
*/
@Component
public class AppPublisher {
@Resource
private ApplicationEventPublisher applicationEventPublisher;
public void publish(){
applicationEventPublisher.publishEvent(new AppEvent(new AppListener(),"publish event"));
}
}
為了方便,這裡直接使用SpringBoot來進行測試,先獲取AppPublisher,然後呼叫publish釋出事件。
@SpringBootApplication
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
AppPublisher publisher = context.getBean(AppPublisher.class);
publisher.publish();
}
}
上面整個事件釋出的程式碼就寫完了,我們可以看出其實還是比較簡單的,裡面最核心的三個元件分別為,事件(Event)
,監聽器(Listener)
,釋出器(Publisher)
,實際使用中我們可以根據自己的需求去實現。
上面我們知道了Spring的事件監聽機制的基本使用,那麼整個事件在Spring中是怎麼流轉的呢,我們很有必要去弄清楚。
我們使用的是SpringBoot專案來進行測試,我們先找到SpringBoot對事件監聽機制進行處理的入口,然後再進行分析,SpringBoot對上下文進行處理的入口類是AbstractApplicationContext,它是Spring的入口,其中我們主要關注的refresh()
方法,因為refresh中的方法比較多,我們下面只保留了三個方法。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Check for listener beans and register them.
registerListeners();
// Last step: publish corresponding event.
finishRefresh();
}
}
}
ApplicationEventMulticaster是一個介面,它定義瞭如何將ApplicationEvent傳遞給事件監聽者(event listener)。該介面有多個實現類,可以使用不同的策略將事件分派給不同的監聽者。
ApplicationEventMulticaster為Spring事件機制的核心之一,它支援在應用中傳遞事件,並且可以將事件廣播給多個監聽者。在Spring中,事件是由ApplicationEvent及其子類表示的,例如ContextStartedEvent和ContextStoppedEvent等。當某些事件發生時,Spring容器將使用事件廣播機制來通知感興趣的監聽者。
這個方法的作用是對ApplicationEventMulticaster進行賦值,Spring在初始化的時候會將ApplicationEventMulticaster註冊進IOC容器,這裡就只是單純從IOC容器中獲取ApplicationEventMulticaster來進行賦值,以方便後續的使用。
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isTraceEnabled()) {
logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
} else {
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isTraceEnabled()) {
logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
}
}
}
這個方法的作用主要就是註冊監聽器,它會從IOC容器獲取到我們註冊的監聽器,然後將其加入到Multicaster中,在AbstractApplicationEventMulticaster中,使用一個Set集合來裝監聽器。
public final Set<String> applicationListenerBeans = new LinkedHashSet<>();
finishRefresh()的作用是釋出事件,裡面是一些釋出事件的邏輯,但是由於我們還沒有正式釋出事件,所以這裡並不會釋出事件,當我們使用applicationEventPublisher的publishEvent方法釋出事件時,才會真正的釋出事件。
上面範例中使用ApplicationEventPublisher的publishEvent釋出事件,最終會進入AbstractApplicationContext類中進行事件釋出,我們只關注最重要的方法multicastEvent(),它是廣播器ApplicationEventMulticaster的一個方法事件都是由廣播器進行釋出。
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
ApplicationEventPublisher並沒有真正釋出事件,它相當於只是抽象了事件的釋出,為了讓我們更加簡單和方便使用,但是真正釋出事件的是ApplicationEventMulticaster,在multicastEvent()方法中,如果我們設定了執行緒池,那麼事件就會被加入執行緒池,從而非同步執行,如果沒有設定執行緒池,那麼就同步執行,最終執行都是呼叫invokeListener()方法。
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
} else {
invokeListener(listener, event);
}
}
}
預設是不會使用執行緒池的,如果我們需要事件非同步執行,那麼可以設定執行緒池,其核心就是給廣播器SimpleApplicationEventMulticaster的成員變數taskExecutor設定
/**
* 功能說明: 事件任務執行緒池
* <p>
* Original @Author: steakliu-劉牌, 2023-06-11 13:17
* <p>
* Copyright (C)2020-2022 steakliu All rights reserved.
*/
@Configuration
public class TaskExecutor {
@Bean("eventTaskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(10);
threadPoolTaskExecutor.setMaxPoolSize(20);
threadPoolTaskExecutor.setKeepAliveSeconds(10);
threadPoolTaskExecutor.setThreadNamePrefix("application-event-thread");
threadPoolTaskExecutor.setQueueCapacity(100);
threadPoolTaskExecutor.setAllowCoreThreadTimeOut(true);
threadPoolTaskExecutor.setAllowCoreThreadTimeOut(true);
threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());
threadPoolTaskExecutor.initialize();
return threadPoolTaskExecutor;
}
@Bean
public ApplicationEventMulticaster applicationEventMulticaster() {
SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
simpleApplicationEventMulticaster.setTaskExecutor(taskExecutor());
return simpleApplicationEventMulticaster;
}
}
invokeListener最終會通過傳入的監聽器去呼叫目標監聽器,也就是我們自定義的監聽器,主要程式碼如下,我們可以看到最終呼叫onApplicationEvent方法,就是我們上面範例AppListener監聽器的onApplicationEvent方法。
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
listener.onApplicationEvent(event);
}
到這裡,整個流程就完了,我們梳理一下重要的元件。
上面的四個元件基本上就是Spring事件監聽機制的全部,ApplicationEvent是事件的規範,ApplicationListener是監聽器,ApplicationEventPublisher是釋出器,ApplicationEventMulticaster是廣播器,其實ApplicationEventMulticaster和ApplicationEventPublisher本質是一樣的,都能完成事件的釋出,ApplicationEventPublisher最終也是去呼叫ApplicationEventMulticaster,只不過它只專注於事件釋出,單獨提出一個介面來,職責更加單一,這也是一種設計思想。
上面對Spring事件監聽機制的使用和原理進行了詳細的介紹,並對其中涉及的元件進行解析,Spring事件監聽機制是一個很不錯的功能,我們在進行業務開發的時候可以引入,在相關的開源框架中也是用它的身影,比如高效能閘道器ShenYu中就使用了Spring事件監聽機制來發布閘道器的更新資料,它可以降低系統的耦合性,使系統的擴充套件性更好。
今天的分享就到這裡,感謝你的觀看,我們下期見,如果文中有說得不合理或者不對的地方,希望你能指出,我們進行交流!