打个广告
个人想写《springboot源码解析》这一系列很久了,但是一直角儿心底的知识积累不足,所以一直没有动笔。
所以想找一些小伙伴一起写这一系列,互相纠错交流学习。
如果有小伙伴有兴趣一起把这一系列的讲解写完的话,加下我微信:13670426148,我们一起完成,当交流学习。
后期还想写一系列介绍rpc框架的,不过要再过一阵子了,先把springboot的写完,坚持一周一更,周末更新
前言
上篇回顾
讲了SpringApplication的实例化,其中最主要的是加载Spring的Listener和Initializer,Listener为10个,Initializer为6个,详细查看 走心Springboot源码解析: 一、SpringApplication的实例化
上篇写完之后才发现之前用的是springboot1.5的版本,但是现在大家都是用springboot2.+,所以我找了个springboot2.0+ 的项目来进行解析,但是第一篇就还没改变,到时候有空再更新,不过大致的内容还是差不多的。
这篇主要讲run()方法,分成两部分,这篇先讲上半部分,主要聚焦在run()方法中涉及到的 事件-监听器-广播 机制,直到创建上下文部分(context = this.createApplicationContext())。
run()方法方法总览
public ConfigurableApplicationContext run(String... args) {
//这是一个计时器,stopWatch.start()开始计时,到后面stopWatch.stop()停止计时。
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//这个ConfigurableApplicationContext就是我们说的 上下文,后面preContext()的时候会详细解析,这个是springboot中最重要的一个类了
ConfigurableApplicationContext context = null;
//自定义SpringApplication启动错误的回调接口
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
//此为设置 System 里面有一个properties的里的值 "java.awt.headless" ,设置为默认值true
this.configureHeadlessProperty();
//这个SpringApplicationRunListeners可跟前面的10个Listener不一样,这里面封装了一个广播。后面会讲他的实例化过程
SpringApplicationRunListeners listeners = this.getRunListeners(args);
//开始进行事件广播,下面详细解析
listeners.starting();
Collection exceptionReporters;
try {
//获取初始化参数,就是我们运行时的初始化参数,比如“java -jar --port=8080” 其中port就是一个参数了
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//environment是运行时环境
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
//打印出springboot的标志,这个可以自己选择要打印输出的文本.
Banner printedBanner = this.printBanner(environment);
//创建根上下文,这个方法特别重要,将是下章讲解的重点
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
//准备环境,下面先不讲了。
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
广播的封装
SpringApplicationRunListeners:一个存SpringApplicationRunListener的集合,里面有些方法,后续都会讲到;
SpringApplicationRunListeners :
| SpringApplicationRunListener
|SimpleApplicationEventMulticaster
SimpleApplicationEventMulticaster
才是真正的广播,SpringApplicationRunListeners只不过是对其的一层封装
SpringApplicationRunListeners的实例化过程
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
//这里就不赘述了,META-INF/spring.factories下
//1. 获取key为SpringApplicationRunListener类路径为key 对应的 value
//2. 实例化value对应的类,最后得到的类是EventPublishingRunListener.class,,如图1.1所示
return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
图1.1:
所以说得到的 SpringApplicationRunListeners 里面有一个实例变量,内容是一个 EventPublishingRunListener 组成的 List 。
- EventPublishingRunListener继承于SpringApplicationRunListener。
- SpringApplicationRunListeners里面有一个实例变量,是多个EventPublishingRunListener组成的1个List。
SpringApplicationRunListener的结构
从命名我们就可以知道它是一个监听者,那纵观整个启动流程我们会发现,它其实是用来在整个启动流程中接收不同执行点事件通知的监听者,SpringApplicationRunListener接口规定了SpringBoot的生命周期,在各个生命周期广播相应的事件,调用实际的ApplicationListener类。
接下来看SpringApplicationRunListener
SpringApplicationRunListener接口规定了SpringBoot的生命周期
public class SpringApplicationRunListener{
//刚执行run方法时
void started();
//环境建立好时候
void environmentPrepared(ConfigurableEnvironment environment);
//上下文建立好的时候
void contextPrepared(ConfigurableApplicationContext context);
//上下文载入配置时候
void contextLoaded(ConfigurableApplicationContext context);
//上下文刷新完成后,run方法执行完之前
void finished(ConfigurableApplicationContext context, Throwable exception);
}
它定义了5个步骤:
- started() -> run方法执行的时候立马执行;对应事件的类型是ApplicationStartedEvent,通知监听器,SpringBoot开始执行
- environmentPrepared() -> ApplicationContext创建之前并且环境信息准备好的时候调用;对应事件的类型是ApplicationEnvironmentPreparedEvent), 通知监听器,Environment准备完成
- contextPrepared -> ApplicationContext创建好并且在source加载之前调用一次;没有具体的对应事件), 通知监听器,ApplicationContext已经创建并初始化完成
- contextLoaded -> ApplicationContext创建并加载之后并在refresh之前调用;对应事件的类型是ApplicationPreparedEvent), 通知监听器,ApplicationContext已经完成IoC配置价值
- finished -> run方法结束之前调用;对应事件的类型是ApplicationReadyEvent或ApplicationFailedEvent), 通知监听器,SpringBoot启动完成
EventPublishingRunListener的构造器
我们来看EventPublishingRunListener的构造器。还有他的两个重要的方法:
EventPublishingRunListener类 实现了SpringApplicationRunListener,它具有广播事件的功能。
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
//这个是参数传进来的,也就是SpringApplication.class
private final SpringApplication application;
//这个是初始化参数
private final String[] args;
//真正的广 播
private final SimpleApplicationEventMulticaster initialMulticaster;
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
//新建立广播器,这个广播器特别重要,下面继续解析
this.initialMulticaster = new SimpleApplicationEventMulticaster();
//application.getListeners()就是获取10个初始化的Listenter,具体可以参见上一篇了解哪10个Listener
Iterator var3 = application.getListeners().iterator();
while(var3.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var3.next();
//添加到内部的defaultRetriever里,详细代码可以自己翻进去查看,这里不拓展,
//后面需要根据事件类型推测有哪些监听器需要被触发,所以就得存把所有的监听器先存起来,称作一个注册表吧
this.initialMulticaster.addApplicationListener(listener);
}
}
}
就是说,EventPublishingRunListener里面有一个广播器,结合上面的SpringApplicationRunListener接口声明的方法,我们可以得到其机制大概是:
- run()方法是用来在整个启动流程中接收不同执行点事件通知的监听者,唤醒监听者
- 而再进去就是调用EventPublishingRunListener的started()、environmentPrepared()等唤醒的,这里面就会有根据不同的方法,started(),或者environmentPrepared()等,生成不同的事件。传递给广播器。
- 而广播器SimpleApplicationEventMulticaster initialMulticaster, 就从注册表中(上文代码说了注册表的形成是那段代码),根据Event的类型,找到那些监听器是需要被触发的,执行其 multicastEvent(ApplicationEvent event) 方法,内部是invokeListener(listener, event);
广播器
multicastEvent() 方法就是广播方法
SimpleApplicationEventMulticaster广播出去,广播出去的事件对象会被SpringApplication中的listeners属性进行处理。
下面先解析一下广播方法multicastEvent() 和 执行方法 invokeListener()
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
//去注册表中根据相应的事件,获取Event对应的Listener。后面会简略讲讲获取的过程
Iterator var4 = this.getApplicationListeners(event, type).iterator();
while(var4.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var4.next();
Executor executor = this.getTaskExecutor();
if (executor != null) {
executor.execute(() -> {
this.invokeListener(listener, event);
});
} else {
//最后的执行方法是这个,参数是“监听器和事件”
this.invokeListener(listener, event);
}
}
}
//执行方法
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = this.getErrorHandler();
if (errorHandler != null) {
try {
//看多了源码你们会发现,spring的源码很喜欢这么写,一般invokeListener的时候不是真的invokeListener的真实过程
//而是会把真正的逻辑放到 doInvokeListener(do****,这里就是doInvokeListener)中执行.
//1. 而在invokeListener中只是在真正处理方法前做一点数据封装,
//2. 或者异常检查
// 目的:个人感觉主要的目的是把真正处理过程的代码缩减,使得真正的处理逻辑变得简洁易懂,不会有多余的代码加重理解难度
this.doInvokeListener(listener, event);
} catch (Throwable var5) {
errorHandler.handleError(var5);
}
} else {
this.doInvokeListener(listener, event);
}
}
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
//这里就很明白了,执行监听器的方法
listener.onApplicationEvent(event);
} catch (ClassCastException var6) {
String msg = var6.getMessage();
if (msg != null && !this.matchesClassCastMessage(msg, event.getClass())) {
throw var6;
}
Log logger = LogFactory.getLog(this.getClass());
if (logger.isDebugEnabled()) {
logger.debug("Non-matching event type for listener: " + listener, var6);
}
}
}
所以总结一下
两种监听器和广播器的关系如下:
SpringApplicationRunListeners、SpringApplicationRunListener、SimpleApplicationEventMulticaster
(1) SpringApplicationRunListeners是SpringApplicationRunListener的封装。pringApplicationRunListeners中包含多个SpringApplicationRunListener,
是为了批量执行的封装,SpringApplicationRunListeners与SpringApplicationRunListener生命周期相同,调用每个周期的各个SpringApplicationRunListener
然后广播利用SimpleApplicationEventMulticaster进行广播。
(2)SimpleApplicationEventMulticaster是封装在SpringApplicationRunListener里面的广播器,
通过上面的3个特点可以看出SpringApplicationRunListener。springboot启动的几个主要过程的监听通知都是通过他来进行回调。
流程图如图1.2所示
其中就可以看出他们之间的关系了。
开始跑第一个事件started
直接把代码看到listeners.starting()
这一行
class SpringApplicationRunListeners {
public void starting() {
//这里获取SpringApplicationRunListeners里面的List<SpringApplicationRunListener>
Iterator var1 = this.listeners.iterator();
//一般情况下,他只是一个,虽然这里是个list,所以这个while里面只会走1次
while(var1.hasNext()) {
SpringApplicationRunListener listener = (SpringApplicationRunListener)var1.next();
//这个就进去到SpringApplicationRunListener里面去了,而此时SpringApplicationRunListener的实现类是EventPublishingRunListener
listener.starting();
}
}
}
/**
** EventPublishingRunListener实现了SpringApplicationRunListener,所以就得实现里面的starting(), environmentPrepared()等,贯穿整个springboot启动流程,而starting()只是他其中最开始的一步,看最上面整体的代码部分就知道了,后面还有environmentPrepared(), contextPrepared()等
**/
pubic class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
public void starting() {
//1. 广播器执行广播事件方法
//2. 参数是包装出来的 ApplicationStartingEvent ,跟下面的environmentPrepared,contextPrepared方法比,他们的事件都类型都不同的
//(1) starting对应的是ApplicationStartingEvent
//(2) environmentPrepared对应的是 ApplicationEnvironmentPreparedEvent
//(3) ApplicationContextInitializedEvent对应的是 ApplicationContextInitializedEvent
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
}
public void contextPrepared(ConfigurableApplicationContext context) {
this.initialMulticaster.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
}
//省略****
}
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
//开始进行广播方法,因为此时是staring()方法出发的,所以这里的envent是在EventPublishingRunListener中new ApplicationContextInitializedEvent,
public void multicastEvent(ApplicationEvent event) {
//1. 第二个参数推断出eventType,,在真正执行广播
this.multicastEvent(event, this.resolveDefaultEventType(event));
}
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) { //推断出类型
ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
//从注册表中根据event类型,获得所有对应的监听器,
//结果获得的所有监听器有:
//(1) LoggingApplicationListener、
//(2) BackgroundPreinitializer、
//(3) DelegatingApplicationListener
//(4) LiquibaseServiceLocatorApplicationListener
//(5)EnableEncryptablePropertiesBeanFactoryPostProcessor
//五种类型的对象。这五个对象的onApplicationEvent都会被调用。
Iterator var4 = this.getApplicationListeners(event, type).iterator();
while(var4.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var4.next();
Executor executor = this.getTaskExecutor();
if (executor != null) {
executor.execute(() -> {
this.invokeListener(listener, event);
});
} else {
//直接看调用方法
this.invokeListener(listener, event);
}
}
}
//这就是最终的调用点,Listener的onApplicationEvent(ApplicationEvent) 方法。
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
listener.onApplicationEvent(event);
} catch (ClassCastException var6) {
String msg = var6.getMessage();
if (msg != null && !this.matchesClassCastMessage(msg, event.getClass())) {
throw var6;
}
Log logger = LogFactory.getLog(this.getClass());
if (logger.isDebugEnabled()) {
logger.debug("Non-matching event type for listener: " + listener, var6);
}
}
}
}
那么这五个监听器的onApplicationEvent都做了些什么了,我这里大概说下,细节的话大家自行去跟源码。
(1) LoggingApplicationListener:初始化日志系统,默认是logback,支持3种,优先级从高到低:logback > log4j > javalog
(2) BackgroundPreinitializer:启动多个线程执行相应的任务,包括验证器、消息转换器等等
(3) DelegatingApplicationListener:此时什么也没做
(4) LiquibaseServiceLocatorApplicationListener:此时什么也没做
(5)EnableEncryptablePropertiesBeanFactoryPostProcessor:仅仅打印了一句日志,其他什么也没做
对了,补充一点注册器的代码
喜欢就看,不喜欢就跳过,
// 返回所有的适合于ApplicationStartedEvent的监听器集合
protected Collection<ApplicationListener<?>> getApplicationListeners(ApplicationEvent event, ResolvableType eventType) {
Object source = event.getSource();
Class<?> sourceType = source != null ? source.getClass() : null;
//根据eventType和sourceType组装一个key即是cacheKey
AbstractApplicationEventMulticaster.ListenerCacheKey cacheKey = new AbstractApplicationEventMulticaster.ListenerCacheKey(eventType, sourceType);
//retriever里面包装了所有适合的监听器。
AbstractApplicationEventMulticaster.ListenerRetriever retriever = (AbstractApplicationEventMulticaster.ListenerRetriever)this.retrieverCache.get(cacheKey);
//下面是双通道机制的单例模式写法,直接看标注注释的那行代码即可
if (retriever != null) {
return retriever.getApplicationListeners();
} else if (this.beanClassLoader == null || ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) && (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader))) {
synchronized(this.retrievalMutex) {
retriever = (AbstractApplicationEventMulticaster.ListenerRetriever)this.retrieverCache.get(cacheKey);
if (retriever != null) {
return retriever.getApplicationListeners();
} else {
//当第一次执行的时候,创建一个 retriever,
retriever = new AbstractApplicationEventMulticaster.ListenerRetriever(true);
//下面进行详细解析;
//直接在这里获取所有适合于ApplicationStartedEvent的监听器集合,并使用retriever.applicationListeners.add(listener);,添加到retriever中,
Collection<ApplicationListener<?>> listeners = this.retrieveApplicationListeners(eventType, sourceType, retriever);
//以cacheKey为key,存到缓存中。
this.retrieverCache.put(cacheKey, retriever);
//返回所有的适合于ApplicationStartedEvent的监听器集合
return listeners;
}
}
} else {
return this.retrieveApplicationListeners(eventType, sourceType, (AbstractApplicationEventMulticaster.ListenerRetriever)null);
}
}
//上面方法的解析,直接在这里获取所有适合于ApplicationStartedEvent的监听器集合。
private Collection<ApplicationListener<?>> retrieveApplicationListeners(ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable
AbstractApplicationEventMulticaster.ListenerRetriever retriever) {
List<ApplicationListener<?>> allListeners = new ArrayList();
LinkedHashSet listeners;
LinkedHashSet listenerBeans;
synchronized(this.retrievalMutex) {
//this.defaultRetriever.applicationListeners是所有默认的监听器,就是那10个默认的监听器,如下图所示
listeners = new LinkedHashSet(this.defaultRetriever.applicationListeners);
listenerBeans = new LinkedHashSet(this.defaultRetriever.applicationListenerBeans);
}
Iterator var7 = listeners.iterator();
while(var7.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var7.next();
//就是在这里进行判断的,具体怎么判断就不进去了,这个不是重点
if (this.supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
retriever.applicationListeners.add(listener);
}
allListeners.add(listener);
}
}
//........篇幅有限,这个其实不重要,直接略过即可
}
准备环境 prepareEnvironment
//todo: 这部分可以用“监听器-事件-广播” 解释一下,下周末有时间再进行补充。
prepareEnvironment(listeners, applicationArguments);
加载SpringBoot配置环境(configurableEnvironment),如果是通过web容器发布,会加载StandardEnvironment。将配置文件(Environment)加入到监听器对象中(SpringApplicationRunListeners)
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
//如果environment不为空直接返回 || 如果是web环境则直接实例化StandardServletEnvironment类 || 如果不是web环境则直接实例化StandardEnvironment类
ConfigurableEnvironment environment = getOrCreateEnvironment();
//配置环境信息
configureEnvironment(environment, applicationArguments.getSourceArgs());
//通知所有的监听者,环境已经准备好了
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
总结
本篇主要讲了“监听器-事件-广播”的涉及到的几个类,展示了一个周期 starting()的运行过程。
再总结一下,流程如下:
- run()方法是用来在整个启动流程中接收不同执行点事件通知的监听者,唤醒监听者
- 而再进去就是调用EventPublishingRunListener的started()、environmentPrepared()等唤醒的,这里面就会有根据不同的方法,started(),或者environmentPrepared()等,生成不同的事件。传递给广播器。
- 而广播器SimpleApplicationEventMulticaster initialMulticaster, 就从注册表中(上文代码说了注册表的形成是那段代码),根据Event的类型,找到那些监听器是需要被触发的,执行其 multicastEvent(ApplicationEvent event) 方法,内部是invokeListener(listener, event);
下期预告
下期打算讲一讲, context的部分,这个内容就有很多了啊。。
参考内容: 大神写的,分析很透彻,站在巨人的肩膀上: