Spring事件監(jiān)聽源碼解析流程分析
spring事件監(jiān)聽機(jī)制離不開容器IOC特性提供的支持,比如容器會(huì)自動(dòng)創(chuàng)建事件發(fā)布器,自動(dòng)識(shí)別用戶注冊(cè)的監(jiān)聽器并進(jìn)行管理,在特定的事件發(fā)布后會(huì)找到對(duì)應(yīng)的事件監(jiān)聽器并對(duì)其監(jiān)聽方法進(jìn)行回調(diào)。Spring幫助用戶屏蔽了關(guān)于事件監(jiān)聽機(jī)制背后的很多細(xì)節(jié),使用戶可以專注于業(yè)務(wù)層面進(jìn)行自定義事件開發(fā)。然而我們對(duì)內(nèi)部的實(shí)現(xiàn)還是有一些疑問,比如:
• 事件發(fā)布器ApplicationEventMulticaster是何時(shí)被初始化的,初始化過程中都做了什么?
• 注冊(cè)事件監(jiān)聽器的過程是怎樣的,容器怎么識(shí)別出它們并進(jìn)行管理?
• 容器發(fā)布事件的流程是怎樣的?它如何根據(jù)發(fā)布的事件找到對(duì)應(yīng)的事件監(jiān)聽器,事件和由該事件觸發(fā)的監(jiān)聽器之間的匹配規(guī)則是怎樣的?
初始化事件發(fā)布器流程
真正的事件發(fā)布器是ApplicationEventMulticaster,它定義在AbstractApplicationContext中,并在ApplicationContext容器啟動(dòng)的時(shí)候進(jìn)行初始化。在容器啟動(dòng)的refrsh()方法中可以找到初始化事件發(fā)布器的入口方法,如下圖所示:
/** * Initialize the ApplicationEventMulticaster. * Uses SimpleApplicationEventMulticaster if none defined in the context. * @see org.springframework.context.event.SimpleApplicationEventMulticaster */ 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() + "]"); } } }
這里會(huì)根據(jù)核心容器beanFactory中是否有id為applicationEventMulticaster
的bean分兩種情況:
(1)容器中已有id為applicationEventMulticaster的bean:直接從容器緩存獲取或是創(chuàng)建該bean實(shí)例,并交由成員變量applicationEventMulticaster保存。當(dāng)用戶自定義了事件發(fā)布器并向容器注冊(cè)時(shí)會(huì)執(zhí)行該流程。
(2)容器中不存在applicationEventMulticaster的bean:這是容器默認(rèn)的執(zhí)行流程,會(huì)創(chuàng)建一個(gè)SimpleApplicationEventMulticaster,其僅在實(shí)現(xiàn)事件發(fā)布器基本功能(管理事件監(jiān)聽器以及發(fā)布容器事件)的前提下,增加了可以設(shè)置任務(wù)執(zhí)行器Executor和錯(cuò)誤處理器ErrorHandler的功能,當(dāng)設(shè)置Executor為線程池時(shí),則會(huì)以異步的方式對(duì)事件監(jiān)聽器進(jìn)行回調(diào),而ErrorHandler允許我們?cè)诨卣{(diào)方法執(zhí)行錯(cuò)誤時(shí)進(jìn)行自定義處理。默認(rèn)情況下,這兩個(gè)變量都為null。
之后會(huì)調(diào)用beanFactory.registerSingleton方法將創(chuàng)建的SimpleApplicationEventMulticaster實(shí)例注冊(cè)為容器的單實(shí)例bean。
初始化事件發(fā)布器總結(jié)一句話:由容器實(shí)例化用戶自定義的事件發(fā)布器或者由容器幫我們創(chuàng)建一個(gè)簡單的事件發(fā)布器并交由容器管理。
注冊(cè)事件監(jiān)聽器流程
注冊(cè)事件監(jiān)聽器的流程在初始化事件發(fā)布器之后,如下圖所示:
/** * Add beans that implement ApplicationListener as listeners. * Doesn't affect other listeners, which can be added without being beans. */ protected void registerListeners() { // 首先注冊(cè)靜態(tài)指定的監(jiān)聽器。 for (ApplicationListener<?> listener : getApplicationListeners()) { getApplicationEventMulticaster().addApplicationListener(listener); } // 不要在這里初始化FactoryBeans:我們需要保留所有常規(guī)Bean // 未初始化以允許后處理器應(yīng)用于它們! String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false); for (String listenerBeanName : listenerBeanNames) { getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName); } // 發(fā)布早期應(yīng)用程序事件 Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents; this.earlyApplicationEvents = null; if (earlyEventsToProcess != null) { for (ApplicationEvent earlyEvent : earlyEventsToProcess) { getApplicationEventMulticaster().multicastEvent(earlyEvent); } } }
容器事件發(fā)布流程
org.springframework.context.support.AbstractApplicationContext#publishEvent(java.lang.Object, ResolvableType)
將給定事件發(fā)布給所有偵聽器
前面說,在啟動(dòng)的時(shí)候如果沒有一個(gè)beanName叫做applicationEventMulticaster的ApplicationEventMulticaster,那使用的就是SimpleApplicationEventMulticaster,該組件會(huì)在容器啟動(dòng)時(shí)被自動(dòng)創(chuàng)建,并以單例的形式存在,管理了所有的事件監(jiān)聽器,并提供針對(duì)所有容器內(nèi)事件的發(fā)布功能。
org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(ApplicationEvent, ResolvableType)
@Override public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { //獲取事件類型 ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); //獲取事件發(fā)布器內(nèi)的任務(wù)執(zhí)行器,默認(rèn)該方法返回null Executor executor = getTaskExecutor(); //遍歷所有和事件匹配的事件監(jiān)聽器 for (ApplicationListener<?> listener : getApplicationListeners(event, type)) { if (executor != null) { //異步回調(diào)監(jiān)聽方法 executor.execute(() -> invokeListener(listener, event)); } else { //同步回調(diào)監(jiān)聽方法 invokeListener(listener, event); } } }
如何根據(jù)事件類型找到匹配的所有事件監(jiān)聽器?
org.springframework.context.event.AbstractApplicationEventMulticaster#getApplicationListeners(ApplicationEvent, ResolvableType)
/** * Return a Collection of ApplicationListeners matching the given * event type. Non-matching listeners get excluded early. * @param event the event to be propagated. Allows for excluding * non-matching listeners early, based on cached matching information. * @param eventType the event type * @return a Collection of ApplicationListeners * @see org.springframework.context.ApplicationListener */ protected Collection<ApplicationListener<?>> getApplicationListeners( ApplicationEvent event, ResolvableType eventType) { // 獲取事件中的事件源對(duì)象 Object source = event.getSource(); // 獲取事件源類型 Class<?> sourceType = (source != null ? source.getClass() : null); // 以事件類型和事件源類型為參數(shù)構(gòu)建一個(gè)cacheKey ,用于從緩存map中獲取與之匹配的監(jiān)聽器列表 ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType); // 根據(jù)cacheKey從緩存中獲取CachedListenerRetriever 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); // 不存在就檢索給定事件和源類型的應(yīng)用程序偵聽器,并放到緩存 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); } }
如果事件時(shí)第一次發(fā)布,會(huì)遍歷所有的事件監(jiān)聽器,并根據(jù)事件類型和事件源類型進(jìn)行匹配:
org.springframework.context.event.AbstractApplicationEventMulticaster#retrieveApplicationListeners
/** * Actually retrieve the application listeners for the given event and source type. * @param eventType the event type * @param sourceType the event source type * @param retriever the ListenerRetriever, if supposed to populate one (for caching purposes) * @return the pre-filtered list of application listeners for the given event and source type */ private Collection<ApplicationListener<?>> retrieveApplicationListeners( ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable ListenerRetriever retriever) { // 存放監(jiān)聽器的列表 List<ApplicationListener<?>> allListeners = new ArrayList<>(); Set<ApplicationListener<?>> listeners; Set<String> listenerBeans; synchronized (this.retrievalMutex) { listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners); listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans); } // 添加以編程方式注冊(cè)的偵聽器, // 包括來自ApplicationListenerDetector的偵聽器(單例bean和內(nèi)部bean)。 for (ApplicationListener<?> listener : listeners) { if (supportsEvent(listener, eventType, sourceType)) { if (retriever != null) { retriever.applicationListeners.add(listener); } allListeners.add(listener); } } // 按bean名稱添加偵聽器,這可能與上面通過編程注冊(cè)的偵聽器重疊, // 但這里可能有額外的元數(shù)據(jù)。 if (!listenerBeans.isEmpty()) { ConfigurableBeanFactory beanFactory = getBeanFactory(); for (String listenerBeanName : listenerBeans) { try { if (supportsEvent(beanFactory, listenerBeanName, eventType)) { ApplicationListener<?> listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class); if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) { if (retriever != null) { if (beanFactory.isSingleton(listenerBeanName)) { retriever.applicationListeners.add(listener); } else { retriever.applicationListenerBeans.add(listenerBeanName); } } allListeners.add(listener); } } else { // 刪除最初來自ApplicationListenerDetector的不匹配偵聽器, // 可能會(huì)被上面額外的BeanDefinition元數(shù)據(jù)(例如工廠方法泛型)排除。 Object listener = beanFactory.getSingleton(listenerBeanName); if (retriever != null) { retriever.applicationListeners.remove(listener); } allListeners.remove(listener); } } catch (NoSuchBeanDefinitionException ex) { // 單一偵聽器實(shí)例(沒有支持bean定義)消失-可能在銷毀階段中期} } } //對(duì)匹配的監(jiān)聽器列表進(jìn)行排序 AnnotationAwareOrderComparator.sort(allListeners); if (retriever != null && retriever.applicationListenerBeans.isEmpty()) { retriever.applicationListeners.clear(); retriever.applicationListeners.addAll(allListeners); } return allListeners; }
容器事件發(fā)布的整個(gè)流程,可以總結(jié)如下:
到此這篇關(guān)于Spring事件監(jiān)聽源碼解析的文章就介紹到這了,更多相關(guān)Spring事件監(jiān)聽內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
RocketMQ消息生產(chǎn)者是如何選擇Broker示例詳解
這篇文章主要為大家介紹了RocketMQ消息生產(chǎn)者是如何選擇Broker示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11在java8中使用流區(qū)分質(zhì)數(shù)與非質(zhì)數(shù)詳解
這篇文章主要介紹了在java8中使用流區(qū)分質(zhì)數(shù)與非質(zhì)數(shù)詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-12-12Jackson庫進(jìn)行JSON?序列化時(shí)遇到了無限遞歸(Infinite?Recursion)的問題及解決方案
使用Jackson庫進(jìn)行JSON序列化時(shí)遇到了無限遞歸(Infinite?Recursion)問題,這是因?yàn)閮蓚€(gè)實(shí)體類ComPointQuotaEntity和?ComPointEntity之間存在雙向關(guān)聯(lián)point和pointQuota相互引用,本文給大家介紹解決方案,感興趣的朋友一起看看吧2025-03-03Java之maven打完jar包之后將jar包放到指定位置匯總
這篇文章主要介紹了Java之maven打完jar包之后將jar包放到指定位置匯總,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04spring boot實(shí)戰(zhàn)之內(nèi)嵌容器tomcat配置
本篇文章主要介紹了Spring Boot 使用內(nèi)嵌的tomcat容器配置,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-01-01