SpringBoot事件監聽機制


一、監聽器模式介紹

將一個監聽器(listener)與特定的控件(如按鈕等)綁定起來,當發生用戶點擊等事件(Event)時,調用監聽器的處理方法,從而響應用戶的動作,就叫做事件/監聽器模式。

從上面的語句中,我們可以看出監聽器模式有三個要素:

  • 事件源

  • 事件對象

  • 監聽器

二、自定義監聽器事件

創建天氣事件接口和下雨、下雪實現類

public interface WeatherEvent {
    String getWeather();
}

public class RainWeatherEvent implements WeatherEvent {
    @Override
    public String getWeather() {
        return "下雨";
    }
}

public class SnowWeatherEvent implements WeatherEvent {
    @Override
    public String getWeather() {
        return "下雪";
    }
}

創建天氣監聽實現接口和下雨、下雪監聽實現類

public interface WeatherListener {
    void onWeatherEvent(WeatherEvent event);
}

public class RainListener implements WeatherListener {
    @Override
    public void onWeatherEvent(WeatherEvent event) {
        if (event instanceof RainWeatherEvent) {
            System.out.println(event.getWeather());
        }
    }
}

public class SnowListener implements WeatherListener {
    @Override
    public void onWeatherEvent(WeatherEvent event) {
        if (event instanceof SnowWeatherEvent) {
            System.out.println(event.getWeather());
        }
    }
}

創建事件廣播器接口和天氣事件廣播實現類

public interface EventMulticaster {
    void multicastEvent(WeatherEvent event);
    void addListener(WeatherListener listener);
    void removeListener(WeatherListener listener);
}

public class WeatherEventMulticaster implements EventMulticaster {

    private List<WeatherListener> listenerList = new ArrayList<>();

    @Override
    public void multicastEvent(WeatherEvent event) {
        System.out.println("==========開始事件廣播==========");
        listenerList.forEach(i -> i.onWeatherEvent(event));
        System.out.println("==========結束事件廣播==========");
    }

    @Override
    public void addListener(WeatherListener listener) {
        listenerList.add(listener);
    }

    @Override
    public void removeListener(WeatherListener listener) {
        listenerList.remove(listener);
    }
}

創建啟動測試類

public class Start {

    public static void main(String[] args) {
        // 創建廣播對象
        EventMulticaster eventMulticaster = new WeatherEventMulticaster();
        // 創建下雨事件監聽對象
        RainListener rainListener = new RainListener();
        // 創建下雪事件監聽對象
        SnowListener snowListener = new SnowListener();
        
        // 添加下雨、下雪監聽事件對象
        eventMulticaster.addListener(rainListener);
        eventMulticaster.addListener(snowListener);
        
        // 廣播下雨事件
        eventMulticaster.multicastEvent(new RainWeatherEvent());
        // 廣播下雪事件
        eventMulticaster.multicastEvent(new SnowWeatherEvent());
        
        // 移除下雨監聽事件對象
        eventMulticaster.removeListener(rainListener);

        // 廣播下雨事件
        // 廣播下雪事件
        eventMulticaster.multicastEvent(new RainWeatherEvent());
        eventMulticaster.multicastEvent(new SnowWeatherEvent());
    }
}

啟動項目,查看控制台輸出:

==========開始事件廣播==========
下雨
==========結束事件廣播==========
==========開始事件廣播==========
下雪
==========結束事件廣播==========
==========開始事件廣播==========
==========結束事件廣播==========
==========開始事件廣播==========
下雪
==========結束事件廣播==========

可以看到當下雨監聽器被移除之后,下雨事件就不能被監聽處理了。

三、SpringBoot 監聽器實現

3.1 監聽器

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
	void onApplicationEvent(E event);
}
  • FunctionalInterfacejdk8 新增的,表示 ApplicationListener 接口只有一個方法,如果大於一個方法,將報錯。
  • 接口中有個泛型 <E extends ApplicationEvent>,繼承自 ApplicationEvent。代表實現接口時,可以聲明對哪些事件(如ApplicationEvent)感興趣,在觸發監聽器的時候,對其他事件進行過濾。

3.2 系統廣播器

public interface ApplicationEventMulticaster {
	// 添加事件監聽器
	void addApplicationListener(ApplicationListener<?> listener);
	// 添加事件監聽器
	void addApplicationListenerBean(String listenerBeanName);
	// 移除指定事件監聽器
	void removeApplicationListener(ApplicationListener<?> listener);
	// 移除指定事件監聽器
	void removeApplicationListenerBean(String listenerBeanName);
	// 移除所有事件監聽器
	void removeAllListeners();
	// 事件廣播
	void multicastEvent(ApplicationEvent event);
	// 事件廣播
	void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);
}

3.3 系統事件

事件名 作用
ApplicationStartingEvent 框架啟動事件
ApplicationEnvironmentPreparedEvent 環境准備完畢事件
ApplicationContextInitializedEvent 上下文初始化
ApplicationPreparedEvent 上下文創建完畢,但是Bean還沒有加載完畢
ApplicationStartedEvent bean 實例化完成,但是未調用 Runners接口
ApplicationReadyEvent 調用 Runners 接口完畢
ApplicationFailedEvent 啟動失敗事件

系統事件發生順序:

3.4 監聽器注冊

SpringApplication 初始化的時候就進行了監聽器注冊

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
	......
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	......
}

3.5 監聽器事件觸發機制

starting 事件為例

public ConfigurableApplicationContext run(String... args) {
	......
	SpringApplicationRunListeners listeners = getRunListeners(args);
	listeners.starting();
	......
}

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

void starting() {
	for (SpringApplicationRunListener listener : this.listeners) {
		listener.starting();
	}
}

SpringApplicationRunListeners 接口定義如下,可以看到申明了多個事件:

public interface SpringApplicationRunListener {
    
	default void starting() {}

	default void environmentPrepared(ConfigurableEnvironment environment) {}

	default void contextPrepared(ConfigurableApplicationContext context) {}

	default void contextLoaded(ConfigurableApplicationContext context) {}

	default void started(ConfigurableApplicationContext context) {}

	default void running(ConfigurableApplicationContext context) {}

	default void failed(ConfigurableApplicationContext context, Throwable exception){}

}

看下默認的實現類 EventPublishingRunListener

public class EventPublishingRunListener{
    ......
    private final SimpleApplicationEventMulticaster initialMulticaster;
    @Override
	public void starting() {
        // 調用廣播器來發送事件
		this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
	}
    ......
}    

進入 multicastEvent 方法

@Override
public void multicastEvent(ApplicationEvent event) {
	multicastEvent(event, resolveDefaultEventType(event));
}

resolveDefaultEventType 是對 event 的包裝,不需要理會。

private ResolvableType resolveDefaultEventType(ApplicationEvent event) {
	return ResolvableType.forInstance(event);
}

回到 multicastEvent 方法

@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
	ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    // 獲得線程池
	Executor executor = getTaskExecutor();
    // getApplicationListeners --> 獲得對當前event感興趣的監聽器列表
	for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
		if (executor != null) {
			executor.execute(() -> invokeListener(listener, event));
		}
		else {
			invokeListener(listener, event);
		}
	}
}

獲得對當前 event 感興趣的監聽器列表:

protected Collection<ApplicationListener<?>> getApplicationListeners(
		ApplicationEvent event, ResolvableType eventType) {
	// 獲取事件來源,這里的 source 就是 SpringApplication
	Object source = event.getSource();
    
	Class<?> sourceType = (source != null ? source.getClass() : null);
	ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);

	// 從緩存中獲取結果
	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)))) {
		// 完全同步構建和緩存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 {
		// 無需 ListenerRetriever 緩存->無需同步
		return retrieveApplicationListeners(eventType, sourceType, null);
	}
}

檢索感興趣的監聽器實現:

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) {     
        // 這些監聽器定義在 spring.factories 文件中
		listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
		listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
	}

	for (ApplicationListener<?> listener : listeners) {
        // 只有對當前 eventType 感興趣的 listerer 才會添加到監聽器列表中
		if (supportsEvent(listener, eventType, sourceType)) {
			if (retriever != null) {
				retriever.applicationListeners.add(listener);
			}
			allListeners.add(listener);
		}
	}

	// 通過 bean 名稱獲取監聽器列表
	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 {
					Object listener = beanFactory.getSingleton(listenerBeanName);
					if (retriever != null) {
						retriever.applicationListeners.remove(listener);
					}
					allListeners.remove(listener);
				}
			}
			catch (NoSuchBeanDefinitionException ex) {
			}
		}
	}
	// 對監聽器列表進行排序
	AnnotationAwareOrderComparator.sort(allListeners);
	if (retriever != null && retriever.applicationListenerBeans.isEmpty()) {
		retriever.applicationListeners.clear();
		retriever.applicationListeners.addAll(allListeners);
	}
	return allListeners;
}

獲取感興趣的事件判斷邏輯

protected boolean supportsEvent(
		ApplicationListener<?> listener, ResolvableType eventType, @Nullable Class<?> sourceType) {
	// 必須是 GenericApplicationListener 監聽器類型,如果不是需要進行轉換
	GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ?
			(GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener));
	return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType));
}

構建為 GenericApplicationListenerAdapter

public GenericApplicationListenerAdapter(ApplicationListener<?> delegate) {
	Assert.notNull(delegate, "Delegate listener must not be null");
	this.delegate = (ApplicationListener<ApplicationEvent>) delegate;
	this.declaredEventType = resolveDeclaredEventType(this.delegate);
}

@Nullable
private static ResolvableType resolveDeclaredEventType(ApplicationListener<ApplicationEvent> listener) {
	ResolvableType declaredEventType = resolveDeclaredEventType(listener.getClass());
	if (declaredEventType == null || declaredEventType.isAssignableFrom(ApplicationEvent.class)) {
		Class<?> targetClass = AopUtils.getTargetClass(listener);
		if (targetClass != listener.getClass()) {
			declaredEventType = resolveDeclaredEventType(targetClass);
		}
	}
	return declaredEventType;
}

@Nullable
static ResolvableType resolveDeclaredEventType(Class<?> listenerType) {
	ResolvableType eventType = eventTypeCache.get(listenerType);
	if (eventType == null) {
		eventType = ResolvableType.forClass(listenerType).as(ApplicationListener.class).getGeneric();
		eventTypeCache.put(listenerType, eventType);
	}
	return (eventType != ResolvableType.NONE ? eventType : null);
}

進入 GenericApplicationListenerAdaptersupportsEventType supportsSourceType 方法

@Override
@SuppressWarnings("unchecked")
public boolean supportsEventType(ResolvableType eventType) {
	if (this.delegate instanceof SmartApplicationListener) {
		Class<? extends ApplicationEvent> eventClass = (Class<? extends ApplicationEvent>) eventType.resolve();
		return (eventClass != null && ((SmartApplicationListener) this.delegate).supportsEventType(eventClass));
	}
	else {
		return (this.declaredEventType == null || this.declaredEventType.isAssignableFrom(eventType));
	}
}	

@Override
public boolean supportsSourceType(@Nullable Class<?> sourceType) {
	return !(this.delegate instanceof SmartApplicationListener) ||
			((SmartApplicationListener) this.delegate).supportsSourceType(sourceType);
}

我們回到 invokeListener 方法的實現上來:

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
	ErrorHandler errorHandler = getErrorHandler();
	if (errorHandler != null) {
		try {
			doInvokeListener(listener, event);
		}
		catch (Throwable err) {
			errorHandler.handleError(err);
		}
	}
	else {
		doInvokeListener(listener, event);
	}
}

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
	try {
        // 發送事件
		listener.onApplicationEvent(event);
	} catch (ClassCastException ex) {
		String msg = ex.getMessage();
		if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
			Log logger = LogFactory.getLog(getClass());
			if (logger.isTraceEnabled()) {
				logger.trace("Non-matching event type for listener: " + listener, ex);
			}
		}
		else {
			throw ex;
		}
	}
}

四、自定義監聽器

自定義監聽器事件也有三種方式,我們依次來實現下。首先我們先創建三個監聽器:

@Order(1)
public class FirstListner implements ApplicationListener<ApplicationStartingEvent> {
    @Override
    public void onApplicationEvent(ApplicationStartingEvent event) {
        System.out.println("run firstListner");
    }
}

public class SecondListner implements ApplicationListener<ApplicationStartingEvent> {
    @Override
    public void onApplicationEvent(ApplicationStartingEvent event) {
        System.out.println("run SecondListner");
    }
}

public class ThirdListner implements ApplicationListener<ApplicationStartingEvent> {
    @Override
    public void onApplicationEvent(ApplicationStartingEvent event) {
        System.out.println("run ThirdListner");
    }
}

4.1 在 application.properties 配置文件中配置

context.listner.classes=com.learn.springboot.listener.FirstListner

4.2 在 META-INF/spring.factories 文件中配置

org.springframework.context.ApplicationListener=com.learn.springboot.listener.SecondListner

4.3 在代碼中配置

public static void main(String[] args) {
    // SpringApplication.run(SpringbootApplication.class, args);
    SpringApplication springApplication = new SpringApplication(SpringbootApplication.class);
    springApplication.addListeners(new ThirdListner());
    springApplication.run();
}

啟動項目,觀察控制台輸出:

run firstListner 
run SecondListner
run ThirdListner
.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.5.RELEASE)


免責聲明!

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



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