SpringBoot深入分析講解監(jiān)聽器模式下
我們來以應(yīng)用啟動(dòng)事件:ApplicationStartingEvent為例來進(jìn)行說明:
以啟動(dòng)類的SpringApplication.run方法為入口,跟進(jìn)SpringApplication的兩個(gè)同名方法后,我們會(huì)看到主要的run方法,方法比較長,在這里只貼出與監(jiān)聽器密切相關(guān)的關(guān)鍵的部分:
SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting();
我們跟進(jìn)這個(gè)starting方法,方法的內(nèi)容如下:
void starting() { for (SpringApplicationRunListener listener : this.listeners) { listener.starting(); } }
這里的listeners已經(jīng)在getRunListeners方法中完成了加載,加載原理類似于系統(tǒng)初始化器,關(guān)于系統(tǒng)初始化器的加載可以參考SpringBoot深入淺出分析初始化器
starting方法邏輯很簡單,就是調(diào)用SpringApplicationRunListener的starting方法。下面繼續(xù)分析這個(gè)starting方法:
我們進(jìn)入了EventPublishingRunListener類(SpringApplicationRunListener 的實(shí)現(xiàn)類)的starting方法:
@Override public void starting() { this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args)); }
這里就使用了廣播器,來廣播新的ApplicationStartingEvent事件。
我們跟進(jìn)這個(gè)multicastEvent方法:
@Override public void multicastEvent(ApplicationEvent event) { multicastEvent(event, resolveDefaultEventType(event)); }
繼續(xù)看同名的方法multicastEvent:
@Override public void multicastEvent(final 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); } } }
這里的ResolvableType 是對(duì)event做了包裝,我們不去關(guān)注;由于我們沒有創(chuàng)建線程池,所以executor是空的。我們重點(diǎn)關(guān)注兩個(gè)部分:
1、getApplicationListeners --> 獲取所有關(guān)注此事件的監(jiān)聽器(※);
2、invokeListener --> 激活監(jiān)聽器;
getApplicationListeners (AbstractApplicationEventMulticaster類中)方法,代碼如下:
protected Collection<ApplicationListener<?>> getApplicationListeners( ApplicationEvent event, ResolvableType eventType) { Object source = event.getSource(); Class<?> sourceType = (source != null ? source.getClass() : null); ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType); // Quick check for existing entry on ConcurrentHashMap... ListenerRetriever retriever = this.retrieverCache.get(cacheKey); if (retriever != null) { return retriever.getApplicationListeners(); } if (this.beanClassLoader == null || (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) && (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) { // Fully synchronized building and caching of a ListenerRetriever synchronized (this.retrievalMutex) { retriever = this.retrieverCache.get(cacheKey); if (retriever != null) { return retriever.getApplicationListeners(); } retriever = new ListenerRetriever(true); Collection<ApplicationListener<?>> listeners = retrieveApplicationListeners(eventType, sourceType, retriever); this.retrieverCache.put(cacheKey, retriever); return listeners; } } else { // No ListenerRetriever caching -> no synchronization necessary return retrieveApplicationListeners(eventType, sourceType, null); } }
入?yún)⒅械膃vent就是ApplicationStartingEvent,sourceType是org.springframework.boot.SpringApplication類。ListenerRetriever類型本人將其視作是一個(gè)保存監(jiān)聽器的容器。
可以看出,程序首先在緩存里面尋找ListenerRetriever類型的retriever,如果沒有找到,加鎖再從緩存里面找一次。這里我們緩存里是沒有內(nèi)容的,所以都不會(huì)返回。
接下來調(diào)用了retrieveApplicationListeners方法,來遍歷所有的監(jiān)聽器。retrieveApplicationListeners方法比較長,我們重點(diǎn)關(guān)注下supportsEvent(listener, eventType, sourceType)方法,該方法用來判斷是否此監(jiān)聽器關(guān)注該事件,過程主要包括,判斷此類型是否是GenericApplicationListener類型,如果不是,則構(gòu)造一個(gè)代理,代理的目的是,通過泛型解析,最終獲得監(jiān)聽器所感興趣的事件。
如果經(jīng)過判斷,監(jiān)聽器對(duì)該事件是感興趣的,則此監(jiān)聽器會(huì)被加入監(jiān)聽器列表中。
protected boolean supportsEvent( ApplicationListener<?> listener, ResolvableType eventType, @Nullable Class<?> sourceType) { GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ? (GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener)); return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType)); }
當(dāng)某個(gè)事件所有的監(jiān)聽器被收集完畢后,multicastEvent(SimpleApplicationEventMulticaster類)方法會(huì)對(duì)事件進(jìn)行傳播。即調(diào)用監(jiān)聽器的通用觸發(fā)接口方法:listener.onApplicationEvent(event);這樣,就完成了這個(gè)事件的傳播。
到此這篇關(guān)于SpringBoot深入分析講解監(jiān)聽器模式下的文章就介紹到這了,更多相關(guān)SpringBoot監(jiān)聽器模式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- springboot?事件監(jiān)聽器的案例詳解
- SpringBoot中使用監(jiān)聽器的方法詳解
- SpringBoot如何監(jiān)控Redis中某個(gè)Key的變化(自定義監(jiān)聽器)
- SpringBoot 過濾器、攔截器、監(jiān)聽器對(duì)比及使用場(chǎng)景分析
- SpringBoot加載應(yīng)用事件監(jiān)聽器代碼實(shí)例
- Springboot項(xiàng)目監(jiān)聽器失效問題解決
- SpringBoot實(shí)現(xiàn)攔截器、過濾器、監(jiān)聽器過程解析
- springboot 用監(jiān)聽器統(tǒng)計(jì)在線人數(shù)案例分析
- SpringBoot定義過濾器、監(jiān)聽器、攔截器的方法
相關(guān)文章
Java數(shù)組隊(duì)列及環(huán)形數(shù)組隊(duì)列超詳細(xì)講解
隊(duì)列是一個(gè)有序列表,可以用數(shù)組和鏈表來實(shí)現(xiàn),隊(duì)列有一個(gè)原則。即:先存入隊(duì)列的數(shù)據(jù)要先取出,后存入的要后取出,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2022-09-09剖析Java中在Collection集合中使用contains和remove為什么要重寫equals
這篇文章主要介紹了Collection集合的contains和remove方法詳解remove以及相關(guān)的經(jīng)驗(yàn)技巧,通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-09-09SSH框架網(wǎng)上商城項(xiàng)目第14戰(zhàn)之商城首頁UI的設(shè)計(jì)
這篇文章主要為大家詳細(xì)介紹了SSH框架網(wǎng)上商城項(xiàng)目第14戰(zhàn)之商城首頁UI的設(shè)計(jì),感興趣的小伙伴們可以參考一下2016-06-06Java源碼解析之SortedMap和NavigableMap
今天帶大家來學(xué)習(xí)Java SortedMap和NavigableMap,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們有很好地幫助,需要的朋友可以參考下2021-05-05Gradle的SpringBoot項(xiàng)目構(gòu)建圖解
這篇文章主要介紹了Gradle的SpringBoot項(xiàng)目構(gòu)建圖解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01Java使用JDBC驅(qū)動(dòng)連接MySQL數(shù)據(jù)庫
這篇文章主要為大家詳細(xì)介紹了Java使用JDBC驅(qū)動(dòng)連接MySQL數(shù)據(jù)庫的具體步驟,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12Java中LambdaQueryWrapper設(shè)置自定義排序代碼示例
這篇文章主要給大家介紹了關(guān)于Java中LambdaQueryWrapper設(shè)置自定義排序的相關(guān)資料,lambdaquerywrapper是MyBatis-Plus框架中的一個(gè)查詢條件構(gòu)造器,它可以用于構(gòu)建自定義的查詢條件,需要的朋友可以參考下2023-12-12