上一章刚刚将SpringApplication的构造函数梳理好,下面将继续讲解run方法,老样子,先贴源码
/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
//1、spring的秒表类,这里是计时用,它不是线程安全的
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//spring应用上下文,也就是我们所说的spring根容器
ConfigurableApplicationContext context = null;
//自定义SpringApplication启动错误的回调接口
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//2、设置jdk系统属性java.awt.headless,默认情况为true即开启;
configureHeadlessProperty();
//3、获取运行时的监听器(EventPublishingRunListener实例)
SpringApplicationRunListeners listeners = getRunListeners(args);
//4、触发启动事件,启动监听器会被调用,一共5个监听器被调用
listeners.starting();
try { //参数封装,也就是在命令行下启动应用带的参数,如--server.port=9000
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
1、StopWatch,可以把它理解为spring的一个工具类,这里做计时用,有start()和stop(),隐藏了System.currentTimeMillis,但它不是线程安全的
2、调用了JDK的java.lang.System,置系统属性java.awt.headless,在我们的例子中该属性会被设置为true,因为我们开发的是服务器程序,一般运行在没有显示器和键盘的环境。
3、获取监听器,到这里说一下
SpringApplicationRunListeners类和SpringApplicationRunListener类的介绍。
SpringApplicationRunListeners内部持有SpringApplicationRunListener集合和1个Log日志类。用于SpringApplicationRunListener监听器的批量执行。
SpringApplicationRunListener看名字也知道用于监听SpringApplication的run方法的执行。
它定义了5个步骤:
- starting(run方法执行的时候立马执行;对应事件的类型是ApplicationStartingEvent)
- environmentPrepared(ApplicationContext创建之前并且环境信息准备好的时候调用;对应事件的类型是ApplicationEnvironmentPreparedEvent)
- contextPrepared(ApplicationContext创建好并且在source加载之前调用一次;没有具体的对应事件)
- contextLoaded(ApplicationContext创建并加载之后并在refresh之前调用;对应事件的类型是ApplicationPreparedEvent)
- finished(run方法结束之前调用;对应事件的类型是ApplicationReadyEvent或ApplicationFailedEvent)
SpringApplicationRunListener目前只有一个实现类EventPublishingRunListener,它把监听的过程封装成了SpringApplicationEvent事件并让内部属性(属性名为multicaster)ApplicationEventMulticaster接口的实现类SimpleApplicationEventMulticaster广播出去,广播出去的事件对象会被SpringApplication中的listeners属性进行处理。
所以说SpringApplicationRunListener和ApplicationListener之间的关系是通过ApplicationEventMulticaster广播出去的SpringApplicationEvent所联系起来的。
点进去getRunListeners,贴出源码
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
细看的话,发现有个getSpringFactoriesInstances方法的调用,这个方法大家还记得吗?getSpringFactoriesInstances在SpringApplication的构造方法中调用了两次,分别用来设置属性List<ApplicationContextInitializer<?>> initializers和List<ApplicationListener<?>> listeners。getSpringFactoriesInstances在第一次被调用时会将类路径下所有的META-INF/spring.factories的文件中的属性进行加载并缓存到SpringFactoriesLoader的缓存cache中,下次被调用的时候就直接从SpringFactoriesLoader的cache中取数据了。这次就是从SpringFactoriesLoader的cache中取SpringApplicationRunListener类型的类(全限定名),然后实例化后返回。我们来跟下这次getSpringFactoriesInstances获取的的内容(spring-boot-2.0.3.RELEASE-sources.jar!/META-INF/spring.factories里的EventPublishingRunListener这一个实现类)
EventPublishingRunListener的构造方法中,构造了一个SimpleApplicationEventMulticaster对象,并将SpringApplication的listeners中的全部listener赋值到SimpleApplicationEventMulticaster对象的属性defaultRetriever(类型是ListenerRetriever)的applicationListeners集合中
总的来说,getRunListeners做了什么事呢?就是获取SpringApplicationRunListener类型的实例(EventPublishingRunListener对象),并封装进SpringApplicationRunListeners对象,然后返回这个SpringApplicationRunListeners对象。说的再简单点,getRunListeners就是准备好了运行时监听器EventPublishingRunListener。源码和之前获取实例方法很相似,这里就不贴出了
4、listeners.starting()启动监听器
构建了一个ApplicationStartingEvent事件,并将其发布出去,其中调用了resolveDefaultEventType方法,该方法返回了一个封装了事件的默认类型(ApplicationStartingEvent)的ResolvableType对象。我们接着往下看,看看这个发布过程做了些什么
@Override
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);
}
}
}
初略的看,就是遍历getApplicationListeners(event, type),然后对每个listener进行invokeListener(listener, event),在进入getApplicationListens看看
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();
}
if (this.beanClassLoader == null ||
(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
// 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);
}
}
从上图可知,主要涉及到3个点:缓存retrieverCache、retrieveApplicationListeners已经retrieveApplicationListeners中调用的supportsEvent方法。流程是这样的:
1、缓存中是否有匹配的结果,有则返回
2、若缓存中没有匹配的结果,则从this.defaultRetriever.applicationListeners中过滤,这个this表示的EventPublishingRunListener对象的属性initialMulticaster(也就是SimpleApplicationEventMulticaster对象,而defaultRetriever.applicationListeners的值也是在EventPublishingRunListener构造方法中初始化的)
3、过滤过程,遍历defaultRetriever.applicationListeners集合,从中找出ApplicationStartingEvent匹配的listener,具体的匹配规则需要看各个listener的supportsEventType方法(有两个重载的方法)
4、将过滤的结果缓存到retrieverCache
5、将过滤出的结果返回回去
拿到给定的时间监听器,就该调用了invokeListener
getApplicationListeners方法过滤出的监听器都会被调用,过滤出来的监听器包括LoggingApplicationListener、BackgroundPreinitializer、DelegatingApplicationListener、LiquibaseServiceLocatorApplicationListener、EnableEncryptablePropertiesBeanFactoryPostProcessor五种类型的对象。这五个对象的onApplicationEvent都会被调用。
那么这五个监听器的onApplicationEvent都做了些什么了,我这里大概说下,细节的话大家自行去跟源码
LoggingApplicationListener:检测正在使用的日志系统,默认是logback,支持3种,优先级从高到低:logback > log4j > javalog。此时日志系统还没有初始化
BackgroundPreinitializer:另起一个线程实例化Initializer并调用其run方法,包括验证器、消息转换器等等
DelegatingApplicationListener:此时什么也没做
LiquibaseServiceLocatorApplicationListener:此时什么也没做