SpringBoot 監聽器觸發機制


一、【問題】SpringBoot 監聽器觸發機制?

獲得監聽器列表流程

Start -> getApplicationListeners -> 是否緩存 -----> (否) --> retrieveApplicationListeners -> 遍歷監聽器 -> supportsEvent ----->是 -->加入符合條件監聽器列表 --> end

 

通用觸發條件

 

 

 

以SpringApplicationRunListeners 為例。進入run方法

 

 

進入starting方法。里面是遍歷所有的SpringApplicationRunListeners 

 

 

內部是調用一個廣播器發送

 

3、進入multicastEvent方法。然后進入resolveDefaultEventType 方法。resolveDefaultEventType方法是對event的包裝,不需要過多關注。

 

 

 

4) 然后進入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);
			}
		}
	}

  

 
        

Executor executor = getTaskExecutor(); 獲得線程池

getApplicationListeners(event, type) 獲得對當前event感興趣的監聽器列表

 

5)getApplicationListeners(event, type) 獲得對當前event感興趣的監聽器列表

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);
		}
	}

  獲得事件的來源Object source = event.getSource();

 source就是SpringApplication

 

 

如果已經在緩存中存在感興趣的監聽器,直接返回

	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();
		}

  

 6、retrieveApplicationListeners(eventType, sourceType, retriever)方法

	private Collection<ApplicationListener<?>> retrieveApplicationListeners(
			ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable ListenerRetriever retriever) {

		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);
		}
		for (ApplicationListener<?> listener : listeners) {
			if (supportsEvent(listener, eventType, sourceType)) {
				if (retriever != null) {
					retriever.applicationListeners.add(listener);
				}
				allListeners.add(listener);
			}
		}
		if (!listenerBeans.isEmpty()) {
			BeanFactory beanFactory = getBeanFactory();
			for (String listenerBeanName : listenerBeans) {
				try {
					Class<?> listenerType = beanFactory.getType(listenerBeanName);
					if (listenerType == null || supportsEvent(listenerType, 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);
						}
					}
				}
				catch (NoSuchBeanDefinitionException ex) {
					// Singleton listener instance (without backing bean definition) disappeared -
					// probably in the middle of the destruction phase
				}
			}
		}
		AnnotationAwareOrderComparator.sort(allListeners);
		if (retriever != null && retriever.applicationListenerBeans.isEmpty()) {
			retriever.applicationListeners.clear();
			retriever.applicationListeners.addAll(allListeners);
		}
		return allListeners;
	}

  

監聽器列表如下圖

 

 這些監聽器定義在SpringBoot中的spring.factories文件中

 

只有對當前eventType感興趣的listerer才會添加到監聽器列表中

for (ApplicationListener<?> listener : listeners) {
			if (supportsEvent(listener, eventType, sourceType)) {
				if (retriever != null) {
					retriever.applicationListeners.add(listener);
				}
				allListeners.add(listener);
			}
		}

 

7、進入 supportsEvent方法

	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));
	}  

 

如果不是GenericApplicationListener,則實例GenericApplicationListener. 然后進入GenericApplicationListener構造函數

 

resolveDeclaredEventType方法 計算感興趣的事件類型。

 

進入smartListener.supportsEventType(eventType)

 

 

this.delegate的值為ConfigFileApplicationListener。

 

ConfigFileApplicationListener實現了SmartApplicationListener接口

 

 

 

8、進入invokeListener(listener, event)方法

 

 

 

 

 
        

 

二、將前面的天氣監聽器在Test類調用改造成SpringBoot的形式

1、原來的Test類

 

 

2、監聽器部分

1) 增加WeatherRunListener

 

 

2)、下雨監聽器增加Component

 

 

3)下雪監聽器增加Component

 

 4) 增加單元測試

 

 5) 查看運行效果

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM