Spring事件監(jiān)聽機(jī)制使用和原理解析
前言
好久沒有更新Spring了,今天來分享一下Spring的事件監(jiān)聽機(jī)制,之前分享過一篇Spring監(jiān)聽機(jī)制的使用,今天從原理上進(jìn)行解析,Spring的監(jiān)聽機(jī)制基于觀察者模式,就是就是我們所說的發(fā)布訂閱模式,這種模式可以在一定程度上實(shí)現(xiàn)代碼的解耦,如果想要實(shí)現(xiàn)系統(tǒng)層面的解耦,那么消息隊(duì)列就是我們的不二選擇,消息隊(duì)列本身也是發(fā)布訂閱模式,只是不同的消息隊(duì)列的實(shí)現(xiàn)方式不一樣。
使用
本文我們將使用接口的方式來實(shí)現(xiàn)。
定義事件
如下定義了一個(gè)事件AppEvent,它繼承了ApplicationEvent類,如果我們要使用Spring的事件監(jiān)聽機(jī)制,那么我們定義的事件必須繼承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; } }
定義事件監(jiān)聽器
事件監(jiān)聽器實(shí)現(xiàn)了ApplicationLister接口,其泛型為ApplicationEvent,因?yàn)橐O(jiān)聽事件,所以必須按照Spring的規(guī)則來,onApplicationEvent方法就是監(jiān)聽到的事件,在這里我們可以進(jìn)行我們的業(yè)務(wù)處理,我們可以看出AppLister我們加上了@Component注解,因?yàn)槭录O(jiān)聽器需要加入Spring IOC容器中才能生效。
/** * 功能說明:事件監(jiān)聽器 * <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()); } }
事件發(fā)布器
有了事件監(jiān)聽器,就需要發(fā)布事件,所以就需要一個(gè)事件發(fā)布器,事件發(fā)布器使用的是ApplicationEventPublisher,使用它的publishEvent方法進(jìn)行事件發(fā)布。
/** * 功能說明:事件發(fā)布器 * <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來進(jìn)行測試,先獲取AppPublisher,然后調(diào)用publish發(fā)布事件。
@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(); } }
上面整個(gè)事件發(fā)布的代碼就寫完了,我們可以看出其實(shí)還是比較簡單的,里面最核心的三個(gè)組件分別為,事件(Event)
,監(jiān)聽器(Listener)
,發(fā)布器(Publisher)
,實(shí)際使用中我們可以根據(jù)自己的需求去實(shí)現(xiàn)。
原理
上面我們知道了Spring的事件監(jiān)聽機(jī)制的基本使用,那么整個(gè)事件在Spring中是怎么流轉(zhuǎn)的呢,我們很有必要去弄清楚。
我們使用的是SpringBoot項(xiàng)目來進(jìn)行測試,我們先找到SpringBoot對事件監(jiān)聽機(jī)制進(jìn)行處理的入口,然后再進(jìn)行分析,SpringBoot對上下文進(jìn)行處理的入口類是AbstractApplicationContext,它是Spring的入口,其中我們主要關(guān)注的refresh()
方法,因?yàn)閞efresh中的方法比較多,我們下面只保留了三個(gè)方法。
@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(); } } }
initApplicationEventMulticaster()
ApplicationEventMulticaster是一個(gè)接口,它定義了如何將ApplicationEvent傳遞給事件監(jiān)聽者(event listener)。該接口有多個(gè)實(shí)現(xiàn)類,可以使用不同的策略將事件分派給不同的監(jiān)聽者。
ApplicationEventMulticaster為Spring事件機(jī)制的核心之一,它支持在應(yīng)用中傳遞事件,并且可以將事件廣播給多個(gè)監(jiān)聽者。在Spring中,事件是由ApplicationEvent及其子類表示的,例如ContextStartedEvent和ContextStoppedEvent等。當(dāng)某些事件發(fā)生時(shí),Spring容器將使用事件廣播機(jī)制來通知感興趣的監(jiān)聽者。
這個(gè)方法的作用是對ApplicationEventMulticaster進(jìn)行賦值,Spring在初始化的時(shí)候會(huì)將ApplicationEventMulticaster注冊進(jìn)IOC容器,這里就只是單純從IOC容器中獲取ApplicationEventMulticaster來進(jìn)行賦值,以方便后續(xù)的使用。
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() + "]"); } } }
registerListeners()
這個(gè)方法的作用主要就是注冊監(jiān)聽器,它會(huì)從IOC容器獲取到我們注冊的監(jiān)聽器,然后將其加入到Multicaster中,在AbstractApplicationEventMulticaster中,使用一個(gè)Set集合來裝監(jiān)聽器。
public final Set<String> applicationListenerBeans = new LinkedHashSet<>();
finishRefresh()
finishRefresh()的作用是發(fā)布事件,里面是一些發(fā)布事件的邏輯,但是由于我們還沒有正式發(fā)布事件,所以這里并不會(huì)發(fā)布事件,當(dāng)我們使用applicationEventPublisher的publishEvent方法發(fā)布事件時(shí),才會(huì)真正的發(fā)布事件。
ApplicationEventPublisher發(fā)布事件
上面示例中使用ApplicationEventPublisher的publishEvent發(fā)布事件,最終會(huì)進(jìn)入AbstractApplicationContext類中進(jìn)行事件發(fā)布,我們只關(guān)注最重要的方法multicastEvent(),它是廣播器ApplicationEventMulticaster的一個(gè)方法事件都是由廣播器進(jìn)行發(fā)布。
protected void publishEvent(Object event, @Nullable ResolvableType eventType) { getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType); }
ApplicationEventMulticaster真正發(fā)布事件
ApplicationEventPublisher并沒有真正發(fā)布事件,它相當(dāng)于只是抽象了事件的發(fā)布,為了讓我們更加簡單和方便使用,但是真正發(fā)布事件的是ApplicationEventMulticaster,在multicastEvent()方法中,如果我們配置了線程池,那么事件就會(huì)被加入線程池,從而異步執(zhí)行,如果沒有設(shè)置線程池,那么就同步執(zhí)行,最終執(zhí)行都是調(diào)用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); } } }
默認(rèn)是不會(huì)使用線程池的,如果我們需要事件異步執(zhí)行,那么可以配置線程池,其核心就是給廣播器SimpleApplicationEventMulticaster的成員變量taskExecutor設(shè)置
/** * 功能說明: 事件任務(wù)線程池 * <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
invokeListener最終會(huì)通過傳入的監(jiān)聽器去調(diào)用目標(biāo)監(jiān)聽器,也就是我們自定義的監(jiān)聽器,主要代碼如下,我們可以看到最終調(diào)用onApplicationEvent方法,就是我們上面示例AppListener監(jiān)聽器的onApplicationEvent方法。
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) { listener.onApplicationEvent(event); }
到這里,整個(gè)流程就完了,我們梳理一下重要的組件。
- ApplicationEvent
- ApplicationListener
- ApplicationEventPublisher
- ApplicationEventMulticaster
上面的四個(gè)組件基本上就是Spring事件監(jiān)聽機(jī)制的全部,ApplicationEvent是事件的規(guī)范,ApplicationListener是監(jiān)聽器,ApplicationEventPublisher是發(fā)布器,ApplicationEventMulticaster是廣播器,其實(shí)ApplicationEventMulticaster和ApplicationEventPublisher本質(zhì)是一樣的,都能完成事件的發(fā)布,ApplicationEventPublisher最終也是去調(diào)用ApplicationEventMulticaster,只不過它只專注于事件發(fā)布,單獨(dú)提出一個(gè)接口來,職責(zé)更加單一,這也是一種設(shè)計(jì)思想。
總結(jié)
上面對Spring事件監(jiān)聽機(jī)制的使用和原理進(jìn)行了詳細(xì)的介紹,并對其中涉及的組件進(jìn)行解析,Spring事件監(jiān)聽機(jī)制是一個(gè)很不錯(cuò)的功能,我們在進(jìn)行業(yè)務(wù)開發(fā)的時(shí)候可以引入,在相關(guān)的開源框架中也是用它的身影,比如高性能網(wǎng)關(guān)ShenYu中就使用了Spring事件監(jiān)聽機(jī)制來發(fā)布網(wǎng)關(guān)的更新數(shù)據(jù),它可以降低系統(tǒng)的耦合性,使系統(tǒng)的擴(kuò)展性更好。
以上就是Spring事件監(jiān)聽機(jī)制使用和原理解析的詳細(xì)內(nèi)容,更多關(guān)于Spring 事件監(jiān)聽的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot鉤子函數(shù)的實(shí)現(xiàn)示例
SpringBoot雖然沒有直接稱為“鉤子函數(shù)”的概念,但可以其他方法實(shí)現(xiàn),本文就來介紹一下SpringBoot鉤子函數(shù)的實(shí)現(xiàn)示例,感興趣的可以了解一下2024-11-11JAVA開發(fā)常用類庫UUID、Optional、ThreadLocal、TimerTask、Base64使用方法與實(shí)例詳
這篇文章主要介紹了JAVA開發(fā)常用類庫UUID、Optional、ThreadLocal、TimerTask、Base64使用方法與實(shí)例詳解,需要的朋友可以參考下2020-02-02java開發(fā)hutool HttpUtil網(wǎng)絡(luò)請求工具使用demo
這篇文章主要為大家介紹了hutool之HttpUtil網(wǎng)絡(luò)請求工具使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07Tomcat服務(wù)無法啟動(dòng)的問題的解決方法
這篇文章主要介紹了Tomcat服務(wù)無法啟動(dòng)的問題的解決方法,需要的朋友可以參考下2014-02-02Java實(shí)現(xiàn)一個(gè)簡單的定時(shí)器代碼解析
這篇文章主要介紹了Java實(shí)現(xiàn)一個(gè)簡單的定時(shí)器代碼解析,具有一定借鑒價(jià)值,需要的朋友可以參考下。2017-12-12