springboot启动-监听器模块

监听器模块简介

  1. springboot在启动过程中会调用监听器模块,将开始事件、环境准备事件、启动完成/失败、准备完成等事件发布出去,客户端可以监听各种类型的事件进行特殊处理。

工作流程

  1. 服务端发布消息入口:SpringApplicationRunListeners,遍历调用SpringApplicationRunListener接口列表
  2. 中间服务商:SpringApplicationRunListener,提供发布消息的接口方法,底层调用ApplicationEventMulticaster的广播接口发布消息
  3. 底层维护者:ApplicationEventMulticaster,维护客户端订阅的监听实例,可以添加、删除监听器,可以将消息广播给对应的监听器
  4. 客户端订阅:ApplicationListener,提供给客户端实现接口

源码分析

  1. 监听器模块主要分为几个核心接口:
    SpringApplicationRunListeners->持有服务端SpringApplicationRunListener列表
    SpringApplicationRunListener->服务端调用监听接口
    ApplicationListener->提供给客户端订阅的监听接口
    ApplicationEventMulticaster->事件广播接口,负责维护客户端订阅的监听接口列表
  2. 我们常监听的springboot事件如下(有序):
    ApplicationStartingEvent
    ApplicationEnvironmentPreparedEvent
    ApplicationFailedEvent
    ApplicationStartedEvent
    ApplicationReadyEvent

SpringApplicationRunListeners

  1. 这是一个入口类,在SpringApplication的run方法中进行调度,下面我们看下SpringApplicationRunListeners的相关源码:
属性:SpringApplicationRunListener集合是springboot启动时从spring.factory配置文件中读取
private final List<SpringApplicationRunListener> listeners;
构造器:
SpringApplicationRunListeners(Log log,
		Collection<? extends SpringApplicationRunListener> listeners) {
	this.log = log;
	// 外部传入SpringApplicationRunListener实例列表
	this.listeners = new ArrayList<>(listeners);
}
核心方法:
public void starting() {
	for (SpringApplicationRunListener listener : this.listeners) {
		listener.starting();
	}
}

public void environmentPrepared(ConfigurableEnvironment environment) {
	for (SpringApplicationRunListener listener : this.listeners) {
		listener.environmentPrepared(environment);
	}
}

public void contextPrepared(ConfigurableApplicationContext context) {
	for (SpringApplicationRunListener listener : this.listeners) {
		listener.contextPrepared(context);
	}
}

public void contextLoaded(ConfigurableApplicationContext context) {
	for (SpringApplicationRunListener listener : this.listeners) {
		listener.contextLoaded(context);
	}
}

public void started(ConfigurableApplicationContext context) {
	for (SpringApplicationRunListener listener : this.listeners) {
		listener.started(context);
	}
}

public void running(ConfigurableApplicationContext context) {
	for (SpringApplicationRunListener listener : this.listeners) {
		listener.running(context);
	}
}

public void failed(ConfigurableApplicationContext context, Throwable exception) {
	for (SpringApplicationRunListener listener : this.listeners) {
		callFailedListener(listener, context, exception);
	}
}

SpringApplicationRunListener

实现类:EventPublishingRunListener

  1. EventPublishingRunListener目前是SpringApplicationRunListener接口的唯一实现类,并通过spring.factory进行读取类路径配置,进行实例化
  2. EventPublishingRunListener内部封装了调用ApplicationEventMulticaster接口的逻辑,实现消息的发布
属性:
private final SpringApplication application;
private final SimpleApplicationEventMulticaster initialMulticaster;
构造器:
public EventPublishingRunListener(SpringApplication application, String[] args) {
	this.application = application;
	this.args = args;
	this.initialMulticaster = new SimpleApplicationEventMulticaster();
	// 将客户端监听器加入SimpleApplicationEventMulticaster实例中进行管理
	for (ApplicationListener<?> listener : application.getListeners()) {
		this.initialMulticaster.addApplicationListener(listener);
	}
}
核心方法:
@Override
public void starting() {
	this.initialMulticaster.multicastEvent(
			new ApplicationStartingEvent(this.application, this.args));
}

@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
	this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
			this.application, this.args, environment));
}

…

ApplicationEventMulticaster

  1. 抽象实现类:ApplicationEventMulticaster,实现了客户端监听器添加、删除的实现逻辑,内部实现了监听器的本地缓存操作,这是比较复杂的一块逻辑
  2. 实现类:SimpleApplicationEventMulticaster,实现了事件广播的具体逻辑

AbstractApplicationEventMulticaster->本地缓存底层实现

属性:
// ListenerCacheKey主要是重写equals和hashcode方法,ListenerRetriever主要是持有客户端监听器实例以及容器中监听器bean列表
final Map<ListenerCacheKey, ListenerRetriever> retrieverCache = new ConcurrentHashMap<>(64);
// 排他锁,用于同步处理逻辑
private Object retrievalMutex = this.defaultRetriever;

首先看下ListenerCacheKey
// 实现了Comparable接口,说明想支持排序功能
private static final class ListenerCacheKey implements Comparable<ListenerCacheKey> {

	private final ResolvableType eventType;

	@Nullable
	private final Class<?> sourceType;

	public ListenerCacheKey(ResolvableType eventType, @Nullable Class<?> sourceType) {
		Assert.notNull(eventType, "Event type must not be null");
		this.eventType = eventType;
		this.sourceType = sourceType;
	}

	// 重写equals方法,通过判断事件类型以及类型是否一致来判断key是否相同,避免同一个客户端监听器被加载到本地缓存多次
	@Override
	public boolean equals(Object other) {
		if (this == other) {
			return true;
		}
		ListenerCacheKey otherKey = (ListenerCacheKey) other;
		return (this.eventType.equals(otherKey.eventType) &&
				ObjectUtils.nullSafeEquals(this.sourceType, otherKey.sourceType));
	}
	
	// 重写hashCode,一个对象要作为Map的key,必须同时重写equals方法和hashCode方法
	@Override
	public int hashCode() {
		return this.eventType.hashCode() * 29 + ObjectUtils.nullSafeHashCode(this.sourceType);
	}

	@Override
	public String toString() {
		return "ListenerCacheKey [eventType = " + this.eventType + ", sourceType = " + this.sourceType + "]";
	}

	// 实现compareTo接口,根据事件类型字符串长度来比较,得到先后顺序
	@Override
	public int compareTo(ListenerCacheKey other) {
		int result = this.eventType.toString().compareTo(other.eventType.toString());
		if (result == 0) {
			if (this.sourceType == null) {
				return (other.sourceType == null ? 0 : -1);
			}
			if (other.sourceType == null) {
				return 1;
			}
			result = this.sourceType.getName().compareTo(other.sourceType.getName());
		}
		return result;
	}
}

再看下ListenerRetriever
private class ListenerRetriever {
	// 持有客户端监听器列表
	public final Set<ApplicationListener<?>> applicationListeners;
	// 支持从容器中根据bean名称读取监听器,这里应该是存储spring管理的监听器单例
	public final Set<String> applicationListenerBeans;

	private final boolean preFiltered;

	public ListenerRetriever(boolean preFiltered) {
		this.applicationListeners = new LinkedHashSet<>();
		this.applicationListenerBeans = new LinkedHashSet<>();
		this.preFiltered = preFiltered;
	}

	public Collection<ApplicationListener<?>> getApplicationListeners() {
		List<ApplicationListener<?>> allListeners = new ArrayList<>(
				this.applicationListeners.size() + this.applicationListenerBeans.size());
		allListeners.addAll(this.applicationListeners);
		if (!this.applicationListenerBeans.isEmpty()) {
			BeanFactory beanFactory = getBeanFactory();
			for (String listenerBeanName : this.applicationListenerBeans) {
				try {
					// 从容器中获取监听器
					ApplicationListener<?> listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class);
					if (this.preFiltered || !allListeners.contains(listener)) {
						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);
		return allListeners;
	}
}

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();
	}
	// 根据双亲委派策略,判断是否是相同的类加载器加载的Bean,如果是则可以进行缓存策略
	if (this.beanClassLoader == null ||
			(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
					(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
		// 使用sychronized锁全部,进行缓存更新操作
		// 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);
	}
}

增删改查监听器:都进行加锁操作,清除缓存数据
@Override
public void addApplicationListener(ApplicationListener<?> listener) {
	synchronized (this.retrievalMutex) {
		// Explicitly remove target for a proxy, if registered already,
		// in order to avoid double invocations of the same listener.
		Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
		if (singletonTarget instanceof ApplicationListener) {
			this.defaultRetriever.applicationListeners.remove(singletonTarget);
		}
		this.defaultRetriever.applicationListeners.add(listener);
		this.retrieverCache.clear();
	}
}

@Override
public void addApplicationListenerBean(String listenerBeanName) {
	synchronized (this.retrievalMutex) {
		this.defaultRetriever.applicationListenerBeans.add(listenerBeanName);
		this.retrieverCache.clear();
	}
}

@Override
public void removeApplicationListener(ApplicationListener<?> listener) {
	synchronized (this.retrievalMutex) {
		this.defaultRetriever.applicationListeners.remove(listener);
		this.retrieverCache.clear();
	}
}

@Override
public void removeApplicationListenerBean(String listenerBeanName) {
	synchronized (this.retrievalMutex) {
		this.defaultRetriever.applicationListenerBeans.remove(listenerBeanName);
		this.retrieverCache.clear();
	}
}

@Override
public void removeAllListeners() {
	synchronized (this.retrievalMutex) {
		this.defaultRetriever.applicationListeners.clear();
		this.defaultRetriever.applicationListenerBeans.clear();
		this.retrieverCache.clear();
	}
}

protected Collection<ApplicationListener<?>> getApplicationListeners() {
	synchronized (this.retrievalMutex) {
		return this.defaultRetriever.getApplicationListeners();
	}
}

SimpleApplicationEventMulticaster->发布消息

public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
	ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
	// 根据事件类型去获取对应的监听器列表
	for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
		// 判断是否支持异步
		Executor executor = getTaskExecutor();
		if (executor != null) {
			executor.execute(() -> invokeListener(listener, event));
		}
		else {
			invokeListener(listener, event);
		}
	}
}

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
	ErrorHandler errorHandler = getErrorHandler();
	// 如果发送异常,是否走错误处理策略,目前这里默认的错误处理策略是:LoggingErrorHandler,即打印“Error calling ApplicationEventListener xxx”日志
	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 {
		// 看到熟悉的onApplicationEvent方法了,这个就是我们客户端监听器实现的方法,将消息通知给客户端
		listener.onApplicationEvent(event);
	}
	catch (ClassCastException ex) {
		String msg = ex.getMessage();
		// 判断导致ClassCastException异常原因是否是event.getClass()类型转换异常
		if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
			// Possibly a lambda-defined listener which we could not resolve the generic event type for
			// -> let's suppress the exception and just log a debug message.
			
			Log logger = LogFactory.getLog(getClass());
			if (logger.isDebugEnabled()) {
				logger.debug("Non-matching event type for listener: " + listener, ex);
			}
		}
		else {
			throw ex;
		}
	}
}

== 比较有意思的一个地方:doInvokeListener方法中catch部分==

  1. 这里可能会出现ClassCastException异常:出现这个异常很可能的原因是这个监听器是通过lamda方式定义的,lamda定义的监听器会监听到所有的消息类型;
  2. 我做了个测试:application.addListeners((ApplicationEnvironmentPreparedEvent event) -> System.out.println("123”)),启动springboot应用,发现确实会触发这个异常,那么为什么会出现这个异常呢?
  3. 原因分析:问题出在获取监听器列表的方法上,不管根据哪种事件类型来获取监听器列表,都会把lamda方式创建的监听器查到,进行代码分析,发现该监听器对每种事件类型都支持监听:
getApplicationListeners->retrieveApplicationListeners->supportsEvent

protected boolean supportsEvent(
		ApplicationListener<?> listener, ResolvableType eventType, @Nullable Class<?> sourceType) {
	
	GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ?
			(GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener));
	// 这里smartListener是GenericApplicationListenerAdapter实例
	// 这里判断逻辑对于lamda监听器始终为true,原因是lamda表达式没法识别具体类型啊,所以把消息类型识别成最父级的类型了,即ApplicationEvent,所以支持所有类型的消息
	return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType));
}

ApplicationListener

  1. 客户端可以自己实现监听器接口,从而对spring相关事件进行监听,可以参考我之前写的一篇关于监听器实现的文章:SpringBoot-事件监听的4种实现方式
  2. 接口定义如下:
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

	/**
	 * Handle an application event.
	 * @param event the event to respond to
	 */
	void onApplicationEvent(E event);

}

设计总结

  1. 监听器模块主要使用了:策略模式、模版方法模式、观察者模式
  2. 使用了本地缓存策略,并保证注册监听器列表更新时可以实时清除缓存,使用锁机制保证操作安全性

经验总结

  1. 最好不要用lamda方式创建监听器(虽然可以实现监听功能),最好显示定义监听器

扩展学习

  1. 对监听器模块源码学习了之后,我尝试自己写了一个类似的监听器模块,更新在github上springboot基于底层代码的一些学习
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章