springboot之ApplicationListener

1:spring中的events

我们知道,事件处理首先是事件,对于事件,spring定义了抽象类org.springframework.context.ApplicationEvent,继承了jdk的类java.util.EventObject,源码如下:

// 该抽象类需要被具体的事件实现才又意义,这也是设置为abstract的原因
public abstract class ApplicationEvent extends EventObject {

	// 事件发生的时间点
	private final long timestamp;

	// 构造函数
	public ApplicationEvent(Object source) {
		// 设置事件源码
		super(source);
		this.timestamp = System.currentTimeMillis();
	}

	// 返回事件发生的时间
	public final long getTimestamp() {
		return this.timestamp;
	}

}

spring中事件主要分为两类,一类是和应用程序上下文相关的事件,一类是和应用程序上下文处理请求相关的事件,其中应用程序上下文相关事件使用抽象类org.springframework.context.event.ApplicationContextEvent表示,源码如下:

org.springframework.context.event.ApplicationContextEvent
// 作为由ApplicationContext所引发事件的基础抽象类
@SuppressWarnings("serial")
public abstract class ApplicationContextEvent extends ApplicationEvent {

	// 构造函数,这里的事件源source就是ApplicationContext
	public ApplicationContextEvent(ApplicationContext source) {
		super(source);
	}

	// 获取触发了事件的source源对象,ApplicationContext
	public final ApplicationContext getApplicationContext() {
		return (ApplicationContext) getSource();
	}

}

其主要的实现类如下图:
在这里插入图片描述
分别是容器关闭,容器刷新,容器启动,容器停止对应的事件。
应用程序上文处理事件相关事件使用抽象类org.springframework.web.context.support.RequestHandledEvent,源码如下:

public class RequestHandledEvent extends ApplicationEvent {

	@Nullable
	private String sessionId;

	@Nullable
	private String userName;

	private final long processingTimeMillis;

	@Nullable
	private Throwable failureCause;

	public RequestHandledEvent(Object source, @Nullable String sessionId, @Nullable String userName,
			long processingTimeMillis) {

		super(source);
		this.sessionId = sessionId;
		this.userName = userName;
		this.processingTimeMillis = processingTimeMillis;
	}
	...snip...
}

先知道有该抽象类即可,不详细了解,用到了在看。

2:spring中的listeners

有了事件,肯定就需要监听器来监听了,在spring中使用的接口是org.springframework.context.ApplicationListener,继承了jdk中的接口java.util.EventListener,源码如下:

// 可以注册自己感兴趣的事件的监听器,当有事件发生时,会自动根据注册的事件类型
// 来进行过滤和触发事件
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

	// 触发一个application context的事件
	void onApplicationEvent(E event);

}

其实现类如ContextRefreshListener,注册的事件就是ContextRefreshEvent,源码如下:

org.springframework.web.servlet.FrameworkServlet.ContextRefreshListener
private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {

	@Override
	public void onApplicationEvent(ContextRefreshedEvent event) {
		FrameworkServlet.this.onApplicationEvent(event);
	}
}

ContextCloserListener监听器对应的事件就是ContextRefreshEvent,源码如下:

protected static class ContextCloserListener implements ApplicationListener<ContextClosedEvent> {}

3:监听器的管理

这里的管理包括,注册,删除,触发等。spring是通过事件广播器来实现的,对应的接口是org.springframework.context.event.ApplicationEventMulticaster,源码如下:

// 通过该接口,可以实现管理一组ApplicationListener对象,并且可以给他们发布事件,ApplicationEventPublisher
// 可以委派ApplicationEventMulticaster来真正的发布事件
public interface ApplicationEventMulticaster {

	// 添加一个监听所有事件的监听器
	void addApplicationListener(ApplicationListener<?> listener);

	// 添加一个监听所有事件的监听器的spring bean
	void addApplicationListenerBean(String listenerBeanName);

	// 从事件通知列表中删除一个监听器
	void removeApplicationListener(ApplicationListener<?> listener);

	// 从事件通知列表中删除一个监听器
	void removeApplicationListenerBean(String listenerBeanName);

	// 删除事件广播器中所有的事件监听器
	void removeAllListeners();

	// 广播给定的application事件到合适的监听器
	void multicastEvent(ApplicationEvent event);

	// 广播给定的application事件到合适的监听器
	void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);
}

4:AbstractApplicationContext相关事件发布

在AbstractApplicationContext实现了容器刷新的相关基础操作,其中就包括容器刷新,关闭时的事件发布,对应的发布事件的方法是org.springframework.context.support.AbstractApplicationContext#publishEvent(org.springframework.context.ApplicationEvent),我们先来看下在AbstractApplicationContext中都被哪里调用了,如下图:
在这里插入图片描述
我们顺着其中的finishRefresh()方法的调用,来看下整个的过程,首先执行到代码org.springframework.context.support.AbstractApplicationContext#finishRefresh,如下:
在这里插入图片描述
源码如下:

org.springframework.context.support.AbstractApplicationContext#finishRefresh
protected void finishRefresh() {
	...snip...

	// <202106091616>
	// 发布刷新完成事件
	publishEvent(new ContextRefreshedEvent(this));

	...snip...
}

<202106091616>处是在刷新容器后发布容器刷新完毕事件,源码如下:

org.springframework.context.support.AbstractApplicationContext#publishEvent(org.springframework.context.ApplicationEvent)
@Override
public void publishEvent(ApplicationEvent event) {
	publishEvent(event, null);
}

继续看publishEvent(event, null):

org.springframework.context.support.AbstractApplicationContext#publishEvent(java.lang.Object, org.springframework.core.ResolvableType)
// 发布指定的事件给所有的监听器
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
	Assert.notNull(event, "Event must not be null");

	ApplicationEvent applicationEvent;
	// 强转为applicationevent,其中的事件源applicaitoncontext,可以通过getSource方法获取
	if (event instanceof ApplicationEvent) {
		applicationEvent = (ApplicationEvent) event;
	}
	else {
		...snip...
	}

	if (this.earlyApplicationEvents != null) {
		...snip...
	}
	else {
		// <202106091632>
		getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
	}
	...snip...
}

<202106091632>getApplicationEventMulticaster()源码如下:

org.springframework.context.support.AbstractApplicationContext#getApplicationEventMulticaster
// 返回内部使用的事件广播器
ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {
	if (this.applicationEventMulticaster == null) {
		throw new IllegalStateException("ApplicationEventMulticaster not initialized - " +
				"call 'refresh' before multicasting events via the context: " + this);
	}
	// 类型是org.springframework.context.event.SimpleApplicationEventMulticaster
	return this.applicationEventMulticaster;
}

multicastEvent()方法源码如下:

org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
	ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
	...snip...
	for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
		...snip...
		else {
			// <202106091642>
			invokeListener(listener, event);
		}
	}
}

<202106091642>处是触发监听器,源码如下:

org.springframework.context.event.SimpleApplicationEventMulticaster#invokeListener
// 通过给定的事件触发监听器
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
	...snip...
	else {
		// <202106091644>
		doInvokeListener(listener, event);
	}
}

<202106091644>处源码如下:

org.springframework.context.event.SimpleApplicationEventMulticaster#doInvokeListener
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
	try {
		// <202106091646>
		// 调用监听器的onApplicationEvent方法
		listener.onApplicationEvent(event);
	}
	catch (ClassCastException ex) {
		...snip...
	}
}

<202106091646>处就是根据具体的监听器类型调用对应的方法了。

5:springboot中的ApplicationListener

通过META-INF/spring.factories配置,如下:
在这里插入图片描述
读取这些信息是在SpringApplication的构造函数org.springframework.boot.SpringApplication#SpringApplication(org.springframework.core.io.ResourceLoader, java.lang.Class<?>...),源码如下:

org.springframework.boot.SpringApplication#SpringApplication(org.springframework.core.io.ResourceLoader, java.lang.Class<?>...)
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
	...snip...
	// <202106091823>
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	...snip...
}

<202106091823>处是通过springboot SPI获取,并设置到org.springframework.boot.SpringApplication#listeners,执行结果如下图:
在这里插入图片描述
可以看到结果是和在META-INF/spring.factories文件中配置的一致的。接下来我们分别看下每个监听器都完成了哪些工作。

5.1:AnsiOutputApplicationListener

用来设置ANSI的彩色输出,一般是让集成开发工具输出彩色日志,使得信息更具可读性,具体我也不很了解,先知道吧。
方法签名如下:

public class AnsiOutputApplicationListener
		implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {}

可以看到对应的事件对象是ApplicationEnvironmentPreparedEvent,是在Environment准备完毕后触发调用,源码如下:

org.springframework.boot.context.config.AnsiOutputApplicationListener#onApplicationEvent
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
	// 获取环境对象,用于获取相关的配置信息
	ConfigurableEnvironment environment = event.getEnvironment();
	// <202106101602>
	Binder.get(environment).bind("spring.output.ansi.enabled", AnsiOutput.Enabled.class)
			.ifBound(AnsiOutput::setEnabled);
	// 通过spring.output.ansi.console-available属性值,设置org.springframework.boot.ansi.AnsiOutput#consoleAvailable
	AnsiOutput.setConsoleAvailable(environment.getProperty("spring.output.ansi.console-available", Boolean.class));
}

<202106101602>Binder.get(environment)源码如下:

org.springframework.boot.context.properties.bind.Binder#get
// 通过给定的environment创建Binder实例
public static Binder get(Environment environment) {
	// ConfigurationPropertySources.get(environment):获取属性源对象PropertySource的迭代器
	// PropertySourcesPlaceholdersResolver:用于替换占位符的类,用于通过环境属性替换占位符
	return new Binder(ConfigurationPropertySources.get(environment),
			new PropertySourcesPlaceholdersResolver(environment));
}

<202106101602>bind("spring.output.ansi.enabled", AnsiOutput.Enabled.class)是使用属性spring.output.ansi.enabled的值来创建泛型类型为AnsiOutput.Enabledorg.springframework.boot.context.properties.bind.BindResult对象实例,如下图:
在这里插入图片描述
<202106101602>ifBound(AnsiOutput::setEnabled)相当于如下代码:

ifBound(new Consumer<Enabled>() {
    @Override
    public void accept(Enabled enabled) {
        AnsiOutput.setEnabled(enabled);
    }
});

其中ifBound源码如下:

org.springframework.boot.context.properties.bind.BindResult#ifBound
public void ifBound(Consumer<? super T> consumer) {
	Assert.notNull(consumer, "Consumer must not be null");
	if (this.value != null) {
		consumer.accept(this.value);
	}
}

结果就是设置org.springframework.boot.ansi.AnsiOutput#enabled的值为AnsiOutput.Enabled。

6:ClasspathLoggingApplicationListener

该监听器在springboot应用程序刚调用SpringApplication#run方法时,在执行具体的准备环境,刷新容器等操作前,通过springboot自定义的SpringApplicationRunListener的org.springframework.boot.SpringApplicationRunListener#starting来完成调用。完成打印应用程序的classpath信息的工作。

6.1:classpath内容测试

  • 获取classpath方法
private static String getClasspath() {
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    if (classLoader instanceof URLClassLoader) {
        return Arrays.toString(((URLClassLoader) classLoader).getURLs());
    }
    return "unknown";
}
  • 运行测试
[file:/Users/xb/Desktop/D/dongsir-dev/java-life-current/java-life/target/classes/, 
file:/Users/xb/Desktop/D/java/maven_repo/junit/junit/4.12/junit-4.12.jar, 
file:/Users/xb/Desktop/D/java/maven_repo/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar, 
file:/Users/xb/Desktop/D/java/maven_repo/org/openjdk/jmh/jmh-core/1.18/jmh-core-1.18.jar, 
...snip...
file:/Users/xb/Desktop/D/java/maven_repo/org/slf4j/slf4j-nop/1.7.29/slf4j-nop-1.7.29.jar]

6.2:主要源码

org.springframework.boot.context.logging.ClasspathLoggingApplicationListener#getClasspath
// 获取classpath信息
private String getClasspath() {
	ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
	if (classLoader instanceof URLClassLoader) {
		return Arrays.toString(((URLClassLoader) classLoader).getURLs());
	}
	return "unknown";
}

org.springframework.boot.context.logging.ClasspathLoggingApplicationListener#supportsEventType
// 判断是否支持该事件
public boolean supportsEventType(ResolvableType resolvableType) {
	Class<?> type = resolvableType.getRawClass();
	if (type == null) {
		return false;
	}
	return ApplicationEnvironmentPreparedEvent.class.isAssignableFrom(type)
			|| ApplicationFailedEvent.class.isAssignableFrom(type);
}

org.springframework.boot.context.logging.ClasspathLoggingApplicationListener#onApplicationEvent
// 执行具体的事件调用
public void onApplicationEvent(ApplicationEvent event) {
	if (logger.isDebugEnabled()) {
		// 分别根据事件的类型打印正常的应用程序启动信息,和启动失败信息
		if (event instanceof ApplicationEnvironmentPreparedEvent) {
			logger.debug("Application started with classpath: " + getClasspath());
		}
		else if (event instanceof ApplicationFailedEvent) {
			logger.debug("Application failed to start with classpath: " + getClasspath());
		}
	}
}

7:BackgroundPreinitializer

该监听器实现了ApplicationListener接口,用于在应用程序启动是提前执行一些耗时的初始化操作,主要源码如下:

// 是否需要执行预初始化任务执行的属性配置
public static final String IGNORE_BACKGROUNDPREINITIALIZER_PROPERTY_NAME = "spring.backgroundpreinitializer.ignore";

// 预初始化任务是否启动的标记
private static final AtomicBoolean preinitializationStarted = new AtomicBoolean(false);

// 用于等待预初始化任务执行完成
private static final CountDownLatch preinitializationComplete = new CountDownLatch(1);

org.springframework.boot.autoconfigure.BackgroundPreinitializer#onApplicationEvent
public void onApplicationEvent(SpringApplicationEvent event) {
	// !Boolean.getBoolean(IGNORE_BACKGROUNDPREINITIALIZER_PROPERTY_NAME):不忽略后台初始化
	// event instanceof ApplicationStartingEvent:事件是ApplicationStartingEvent类型
	// preinitializationStarted.compareAndSet(false, true):设置后台执行任务已经启动
	if (!Boolean.getBoolean(IGNORE_BACKGROUNDPREINITIALIZER_PROPERTY_NAME)
			&& event instanceof ApplicationStartingEvent && preinitializationStarted.compareAndSet(false, true)) {
		// 执行提前初始化
		performPreinitialization();
	}
	if ((event instanceof ApplicationReadyEvent || event instanceof ApplicationFailedEvent)
			&& preinitializationStarted.get()) {
		try {
			preinitializationComplete.await();
		}
		catch (InterruptedException ex) {
			Thread.currentThread().interrupt();
		}
	}
}

8:DelegatingApplicationListener

该类的功能类似于DelegatingApplicationContextInitializer,DelagatingApplicationContextInitializer是用来读取通过环境变量context.initializer.classes配置的ApplicationContextInitializer,DelegatingApplicationListener是用来读取通过环境变量context.listener.classes配置的ApplicationListener。源码如下:

org.springframework.boot.context.config.DelegatingApplicationListener#onApplicationEvent
public void onApplicationEvent(ApplicationEvent event) {
	if (eventinstanceof ApplicationEnvironmentPreparedEvent) {
		// <202106111106>
		List<ApplicationListener<ApplicationEvent>> delegates = getListeners(
				((ApplicationEnvironmentPreparedEvent) event).getEnvironment());
		// 没有,直接return
		if (delegates.isEmpty()) {
			return;
		}
		// 创建事件广播器,注册和分发事件
		this.multicaster = new SimpleApplicationEventMulticaster();
		// 循环添加监听器到事件广播器中
		for (ApplicationListener<ApplicationEvent> listener : delegates) {
			this.multicaster.addApplicationListener(listener);
		}
	}
	if (this.multicaster != null) {
		// 通过事件广播器广播事件到监听器中
		this.multicaster.multicastEvent(event);
	}
}

<202106111106>处源码如下:

org.springframework.boot.context.config.DelegatingApplicationListener#getListeners
private List<ApplicationListener<ApplicationEvent>> getListeners(ConfigurableEnvironment environment) {
	if (environment == null) {
		return Collections.emptyList();
	}
	// 获取private static final String PROPERTY_NAME = "context.listener.classes";
	// 配置的属性值
	String classNames = environment.getProperty(PROPERTY_NAME);
	List<ApplicationListener<ApplicationEvent>> listeners = new ArrayList<>();
	if (StringUtils.hasLength(classNames)) {
		// 逗号分隔转Set集合,并循环
		for (String className : StringUtils.commaDelimitedListToSet(classNames)) {
			try {
				// 获className对应的class
				Class<?> clazz = ClassUtils.forName(className, ClassUtils.getDefaultClassLoader());
				// 断言是否为ApplicationListener的子类
				Assert.isAssignable(ApplicationListener.class, clazz,
						"class [" + className + "] must implement ApplicationListener");
				// 实例化,并添加到结果集合中
				listeners.add((ApplicationListener<ApplicationEvent>) BeanUtils.instantiateClass(clazz));
			}
			catch (Exception ex) {
				...snip...
			}
		}
	}
	// 排序
	AnnotationAwareOrderComparator.sort(listeners);
	return listeners;
}

9:ClearCachesApplicationListener

该监听器用来在容器完成刷新后,即在finishRefresh方法中,清空相关的缓存,对应的事件是ContextRefreshedEvent,源码如下:

org.springframework.boot.ClearCachesApplicationListener#onApplicationEvent
public void onApplicationEvent(ContextRefreshedEvent event) {
	// 清空反射使用的相关缓存信息
	ReflectionUtils.clearCache();
	// <202106111516>
	clearClassLoaderCaches(Thread.currentThread().getContextClassLoader());
}

<202106111516>处是清空类加载器相关缓存,源码如下:

org.springframework.boot.ClearCachesApplicationListener#clearClassLoaderCaches
private void clearClassLoaderCaches(ClassLoader classLoader) {
	// 因为会递归调用,所以这里加个结束条件
	if(classLoader == null) {
		return;
	}
	try {
		// 获取classLoader的clearCache方法
		Method clearCacheMethod = classLoader.getClass().getDeclaredMethod("clearCache");
		// 调用classLoader的clearCache方法,清空缓存
		clearCacheMethod.invoke(classLoader);
	}
	catch (Exception ex) {
		// Ignore
	}
	// 递归调用
	clearClassLoaderCaches(classLoader.getParent());
}

10:FileEncodingApplicationListener

该类是检测属性配置file.encoding是否和spring.mandatory-file-encoding一致(如果有该属性的话),如果是不一致,则抛出异常,我本机file.encoding=UTF-8,通过-D设置spring.mandatory-file-encoding为GBK,如下:
在这里插入图片描述
然后运行,如下图:
在这里插入图片描述
看下源码:

org.springframework.boot.context.FileEncodingApplicationListener#onApplicationEvent
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
	// 获取环境对象
	ConfigurableEnvironment environment = event.getEnvironment();
	if (!environment.containsProperty("spring.mandatory-file-encoding")) {
		return;
	}
	// 获取系统file.encoding的值
	String encoding = System.getProperty("file.encoding");
	// 通过spring.mandatory-file-encoding获取配置期望的编码
	String desired = environment.getProperty("spring.mandatory-file-encoding");
	// 不相同,则打印error日志,并抛出异常信息
	if (encoding != null && !desired.equalsIgnoreCase(encoding)) {
		logger.error("System property 'file.encoding' is currently '" + encoding + "'. It should be '" + desired
				+ "' (as defined in 'spring.mandatoryFileEncoding').");
		logger.error("Environment variable LANG is '" + System.getenv("LANG")
				+ "'. You could use a locale setting that matches encoding='" + desired + "'.");
		logger.error("Environment variable LC_ALL is '" + System.getenv("LC_ALL")
				+ "'. You could use a locale setting that matches encoding='" + desired + "'.");
		throw new IllegalStateException("The Java Virtual Machine has not been configured to use the "
				+ "desired default character encoding (" + desired + ").");
	}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值