run方法核心流程
在分析和学习整个run方法之前,我们可以通过以下流程图来看下SpringApplication调用的run方法处理的核心操作包含哪些。
从上面的流程图中可以看出,SpringApplication在run方法中重点做了以下几步操作
- 获取监听器和参数配置
- 打印banner信息
- 创建并初始化容器
- 监听器发送通知
除了以上核心操作,run方法运行过程中还设计启动时长统计,异常报告,启动日志,异常处理等辅助操作。
对照流程图,我们再来整体看下入口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) {
//创建stopWatch对象,用于统计run方法启动时长
StopWatch stopWatch = new StopWatch();
//启动统计
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//配置headless属性
configureHeadlessProperty();
//获得SpringApplicationListener数组
//该数组封装于SpringApplicationRunListeners对象的listeners中
SpringApplicationRunListeners listeners = getRunListeners(args);
//启动监听,遍历SpringApplicationRunListener数组每个元素,并执行
listeners.starting();
try {
//创建ApplicationArguments对象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//加载属性配置,包括所有的配置属性(如application.properties中和外部的属性配置)
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
//打印banner
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);
//调用ApplicationRunner和CommandLineRunner的运行方法
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;
}
SpringApplicationRunListener监听器
监听器的加载与配置
SpringApplicationRunListeners可以理解为一个SpringApplicationRunListener的容器,它将SpringApplicationRunListener的集合以构造方法传入,并赋值给其listeners成员变量,然后提供了针对listeners成员变量的各种遍历操作方法,比如,遍历集合并调用对应的starting,started,running等方法。
SpringApplicationRunListeners的构造方法很简单,其源码如下:
private SpringApplicationRunListeners getRunListeners(String[] args) {
//构造Class数组
Class<?>[] types = new Class<?>[] {
SpringApplication.class, String[].class };
//调用SpringApplicationRunListeners构造方法
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
SpringApplicationRunListeners 构造方法的第二个参数便是SpringApplicationRunListener的集合,SpringApplication中调用构造方法时该参数是通过getSpringFactoriesInstances方法1获取的,代码如下:
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
//获得类加载器
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
//加载META-INF/spring.factories中对应监听器的配置,并将结果存于Set中(去重)
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//实例化监听器
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
//排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
通过方法名可知,getSpringFactoriesInstances是用来获取factories配置文件中的注册类,并进行实例化操作。SpringApplicationRunListener的注册配置位于spring-boot项目中的spring.factories文件内,SringBoot默认仅有一个监听器进行了注册,至于为什么会仅有一个,后面来说哈。
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
下面继续看createSpringFactoriesInstances方法的源码:
@SuppressWarnings("unchecked")
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
ClassLoader classLoader, Object[] args, Set<String> names) {
List<T> instances = new ArrayList<>(names.size());
for (String name : names) {
try {
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
//获取有参构造器
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
T instance = (T) BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}
在上面的代码中,实例化监听器需要有一个默认的构造方法,且构造方法的参数为Class<?>[] parameterTypes。向上追踪该参数的来源,会发现该参数的1值为Class数组,数组的内容依次为SpringApplication.class和String[].class。也就是,SpringApplicationRunListener的实现类必须有默认的构造方法,且构造方法的参数必须依次为SpringApplication和String[]类型。
SpringApplicationRunListener源码解析
接口SpringApplicationRunListener是SpringApplication的run方法监听器。SpringApplicationRunListene提供了一系列的方法,用户可以通过回调这些方法,在启动各个流程时加入指定的逻辑处理。下面解释下该接口都定义了那些待实现的方法及功能。
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.