我们知道,事件处理首先是事件
,对于事件,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...
}
先知道有该抽象类即可,不详细了解,用到了在看。
有了事件,肯定就需要监听器来监听了,在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> {}
这里的管理
包括,注册,删除,触发等。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);
}
在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>
处就是根据具体的监听器类型调用对应的方法了。
通过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文件中配置的一致的。接下来我们分别看下每个监听器都完成了哪些工作。
用来设置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.Enabled
的org.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。
该监听器在springboot应用程序刚调用SpringApplication#run方法时,在执行具体的准备环境,刷新容器等操作前,通过springboot自定义的SpringApplicationRunListener的org.springframework.boot.SpringApplicationRunListener#starting
来完成调用。完成打印应用程序的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]
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());
}
}
}
该监听器实现了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();
}
}
}
该类的功能类似于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;
}
该监听器用来在容器完成刷新后,即在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());
}
该类是检测属性配置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 + ").");
}
}