springboot启动-监听器模块
监听器模块简介
- springboot在启动过程中会调用监听器模块,将开始事件、环境准备事件、启动完成/失败、准备完成等事件发布出去,客户端可以监听各种类型的事件进行特殊处理。
工作流程
- 服务端发布消息入口:SpringApplicationRunListeners,遍历调用SpringApplicationRunListener接口列表
- 中间服务商:SpringApplicationRunListener,提供发布消息的接口方法,底层调用ApplicationEventMulticaster的广播接口发布消息
- 底层维护者:ApplicationEventMulticaster,维护客户端订阅的监听实例,可以添加、删除监听器,可以将消息广播给对应的监听器
- 客户端订阅:ApplicationListener,提供给客户端实现接口
源码分析
- 监听器模块主要分为几个核心接口:
SpringApplicationRunListeners->持有服务端SpringApplicationRunListener列表
SpringApplicationRunListener->服务端调用监听接口
ApplicationListener->提供给客户端订阅的监听接口
ApplicationEventMulticaster->事件广播接口,负责维护客户端订阅的监听接口列表 - 我们常监听的springboot事件如下(有序):
ApplicationStartingEvent
ApplicationEnvironmentPreparedEvent
ApplicationFailedEvent
ApplicationStartedEvent
ApplicationReadyEvent
SpringApplicationRunListeners
- 这是一个入口类,在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
- EventPublishingRunListener目前是SpringApplicationRunListener接口的唯一实现类,并通过spring.factory进行读取类路径配置,进行实例化
- 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
- 抽象实现类:ApplicationEventMulticaster,实现了客户端监听器添加、删除的实现逻辑,内部实现了监听器的本地缓存操作,这是比较复杂的一块逻辑
- 实现类: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部分==
- 这里可能会出现ClassCastException异常:出现这个异常很可能的原因是这个监听器是通过lamda方式定义的,lamda定义的监听器会监听到所有的消息类型;
- 我做了个测试:application.addListeners((ApplicationEnvironmentPreparedEvent event) -> System.out.println("123”)),启动springboot应用,发现确实会触发这个异常,那么为什么会出现这个异常呢?
- 原因分析:问题出在获取监听器列表的方法上,不管根据哪种事件类型来获取监听器列表,都会把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
- 客户端可以自己实现监听器接口,从而对spring相关事件进行监听,可以参考我之前写的一篇关于监听器实现的文章:SpringBoot-事件监听的4种实现方式
- 接口定义如下:
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
/**
* Handle an application event.
* @param event the event to respond to
*/
void onApplicationEvent(E event);
}
设计总结
- 监听器模块主要使用了:策略模式、模版方法模式、观察者模式
- 使用了本地缓存策略,并保证注册监听器列表更新时可以实时清除缓存,使用锁机制保证操作安全性
经验总结
- 最好不要用lamda方式创建监听器(虽然可以实现监听功能),最好显示定义监听器
扩展学习
- 对监听器模块源码学习了之后,我尝试自己写了一个类似的监听器模块,更新在github上springboot基于底层代码的一些学习