我们都知道,我们平时启动springboot项目都是通过在main方法中运行SpringApplication.run来启动的,今天我们通过源码去详细了解一下springboot的启动加载流程
开始看代码之前,我们回顾一下我们平时在使用springboot时的一些问题,然后带着问题去看代码。
1、springboot的默认配置文件application.properties是如何加载的
2、springboot是如何进行bean加载的
3、springboot在启动过程中还做了哪些动作
STEP1 程序启动入口
public static void main(String[] args) {
SpringApplication.run(ACTestPlatform.class, args);
}
public static ConfigurableApplicationContext run(Class<?> primarySource,
String... args) {
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args) {
return new SpringApplication(primarySources).run(args);
}
以上是run方法的调用链路,我们可以发现它最终调用了SpringApplication的构造方法实例化,然后执行run方法。接下来我们去具体看一下它在实例化和执行run方法时具体做了哪些事情
STEP2 实例化SpringApplication对象
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
#推断web应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
#加载工厂实例
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
#加载监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
#推断启动类
this.mainApplicationClass = deduceMainApplicationClass();
}
我们发现,SpringApplication的构造方法做的主要事情就是为一些重要属性赋值。接下来我们逐个分析这些属性是如何赋值的
1、WebApplicationType.deduceFromClasspath() 推断web应用类型
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
这段代码中会去判断一些定义的类名常量在项目中是否存在,从而判断属于不同的web应用。具体的判断逻辑为:
- 如果存在reactive包且不存在servlet包,则为REACTIVE类型
- 如果不存在servlet包,则为NONE类型
- 否则为SERVLET类型
2、getSpringFactoriesInstances(ApplicationContextInitializer.class) 加载spring工厂实例
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
3、实例化步骤
- 通过SpringFactoriesLoader.loadFactoryNames方法去加载所有META-INF/spring.factories文件中工厂实例,存放在MultiValueMap中,key和value是一对多关系,key是接口名,value是实现类名。
- 在map中查找ApplicationContextInitializer对应的值。
- 通过createSpringFactoriesInstances方法去创建所有初始化器的实例
- 对实例进行排序
4、getSpringFactoriesInstances(ApplicationListener.class) 加载所有监听器
和加载工厂实例一致,不再赘述
5、mainApplicationClass = deduceMainApplicationClass() 推断启动类
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
调用main方法所在类作为启动类
STEP3 调用run方法
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//该方法会通过SpringFactoriesLoader.loadFactoryNames方法去获取SpringApplicationRunListener对应的资源,并实例化。我们发现只有一个实现类EventPublishingRunListener,实例化过程中会为其initialMulticaster属性赋值,将step2中加载到的所有监听器存放到对象initialMulticaster的applicationListeners属性中
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
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;
}
run方法中的方法调用比较多,我们这里重点关注几个关键的点
1、通过监听器事件发布初始化
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
这段代码首先通过getRunListeners方法去在加载的META-INF/spring.factories中配置的资源,获取接口名为SpringApplicationRunListener的接口实现类,我们发现只有一个实现EventPublishingRunListener。
springboot在实例化该实现类时,在构造方法中为它的initialMulticaster属性赋值,initialMulticaster属性是一个对象,类型为SimpleApplicationEventMulticaster,值存放在该对象的defaultRetriever(对象,类型为ListenerRetriever)的applicationListeners属性中。
此处会将前面加载到SpringApplication的listeners属性中的所有监听器遍历赋值给applicationListeners。
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
this.initialMulticaster = new SimpleApplicationEventMulticaster();
for (ApplicationListener<?> listener : application.getListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
调用listeners.starting()方法时,会去遍历上一步赋值的applicationListeners属性,为所有监听器发布ApplicationStartingEvent事件,做一些初始化动作
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);
}
}
}
2、加载配置
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
通过prepareEnvironment方法加载配置
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
//创建ConfigurableEnvironment对象,通过不同的webApplicationType类型选择不
同的子类实例化。实例化过程中会加载系统配置信息和环境变量(System.getProperties、System.getenv)
ConfigurableEnvironment environment = getOrCreateEnvironment();
//配置environment,设置转换器,添加active profile等
configureEnvironment(environment, applicationArguments.getSourceArgs());
//发布ApplicationEnvironmentPreparedEvent事件。在ConfigFileApplicationListener
这个监听器中,会加载默认配置文件,文件名可以为application.xml|properties|yml|yaml,
根据不同文件类型调用不同的解析器去解析
listeners.environmentPrepared(environment);
//这个方法会去将配置文件中spring.main对应的配置绑定的SpringApplication这个类的相关属性上去。具体做法为通过反射去获取当前类及所有祖先类(到Object为止)中所有get、is、set开头的方法,将符合条件的方法和属性组装成一个BeanProperty对象。然后去遍历之前加载的所有配置资源中的所有配置,将以spring.main开头的属性值拿到,然后将经过转换器做了类型转换的值通过反射赋给SpringApplication的属性
bindToSpringApplication(environment);
//环境转换
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}