Spring Boot执行流程
每个Spring Boot项目都有一个主程序启动类,在主程序启动类中有一个启动项目的main()方法,在该方法中通过执行SpringApplication.run()即可启动整个Spring Boot程序。那么SpringApplication.run()方法到底是如何做到启动Spring Boot项目的呢?下面我们查看run()方法内部的源码,核心代码具体如下。
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);
}
从上述源码可以看出,SpringApplication.run()方法内部执行了两个操作,分别是SpringApplication实例的初始化创建和调用run()启动项目,这两个阶段的实现具体说明如下。
1.SpringApplication实例的初始化创建
查看SpringApplication实例对象初始化创建的源码信息,核心代码具体如下。
public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = new HashSet();
this.isCustomEnvironment = false;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.setInitializers(this.getSpringFactoriesInstances(
ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}
从上述源码可以看出,SpringApplication的初始化过程主要包括4部分,具体说明如下。
(1)this.webApplicationType = WebApplicationType.deduceFromClasspath()
用于判断当前webApplicationType应用的类型。deduceFromClasspath()方法用于查看Classpath类路径下是否存在某个特征类,从而判断当前webApplicationType类型是SERVLET应用(Spring 5之前的传统MVC应用)还是REACTIVE应用(Spring 5开始出现的WebFlux交互式应用)。
(2)this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class))
用于SpringApplication应用的初始化器设置。在初始化器设置过程中,会使用Spring类加载器SpringFactoriesLoader从META-INF/spring.factories类路径下的META-INF下的spring.factores文件中获取所有可用的应用初始化器类ApplicationContextInitializer。
(3)this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class))
用于SpringApplication应用的监听器设置。监听器设置的过程与上一步初始化器设置的过程基本一样,也是使用SpringFactoriesLoader从META-INF/spring.factories类路径下的META-INF下的spring.factores文件中获取所有可用的监听器类ApplicationListener。
(4)this.mainApplicationClass = this.deduceMainApplicationClass()
用于推断并设置项目main()方法启动的主程序启动类。
2.项目的初始化启动
分析完(new SpringApplication(primarySources)).run(args)源码前一部分SpringApplication实例对象的初始化创建后,查看run(args)方法执行的项目初始化启动过程,核心代码具体如下。
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
//(1)获取SpringApplication初始化的SpringApplicationRunListener运行监听器并运行
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
Collection exceptionReporters;
try {
ApplicationArguments applicationArguments =
new DefaultApplicationArguments(args);
//(2)项目运行环境Environment的预配置
ConfigurableEnvironment environment =
this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
Banner printedBanner = this.printBanner(environment);
//(3)项目应用上下文ApplicationContext的预配置
context = this.createApplicationContext();
exceptionReporters =
this.getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[]{ConfigurableApplicationContext.class}, new Object[]{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);
}
//(4)由项目运行监听器启动配置好的应用上下文ApplicationContext
listeners.started(context);
//(5)调用应用上下文ApplicationContext中配置的程序执行器XxxRunner
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
//(6)由项目运行监听器持续运行配置好的应用上下文ApplicationContext
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters,
(SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
从上述源码可以看出,项目初始化启动过程大致包括以下部分:
(1)this.getRunListeners(args)和listeners.starting()方法主要用于获取SpringApplication实例初始化过程中初始化的SpringApplicationRunListener监听器并运行。
(2)this.prepareEnvironment(listeners, applicationArguments)方法主要用于对项目运行环境进行预设置,同时通过this.configureIgnoreBeanInfo(environment)方法排除一些不需要的运行环境。
(3)this.createApplicationContext()方法及下面加粗部分代码,主要作用是对项目应用上下文ApplicationContextt的预配置,包括先创建应用上下文环境ApplicationContext,接着使用之前初始化设置的context(应用上下文环境)、environment(项目运行环境)、listeners(运行监听器)、applicationArguments(项目参数)和printedBanner(项目图标信息)进行应用上下文的组装配置,并刷新配置。
(4)listeners.started(context)方法用于运行监听器SpringApplicationRunListener启动配置好的应用上下文ApplicationContext。
(5)this.callRunners(context, applicationArguments)方法用于调用项目中自定义的执行器XxxRunner类,使得在项目启动完成后立即执行一些特定程序。其中,Spring Boot提供的执行器接口有ApplicationRunner 和CommandLineRunner两种,在使用时只需要自定义一个执行器类实现其中一个接口并重写对应的run()方法接口,然后Spring Boot项目启动后会立即执行这些特定程序。
(6)listeners.running(context)方法表示在前面一切初始化启动都没有问题的情况下,使用运行监听器SpringApplicationRunListener持续运行配置好的应用上下文ApplicationContext,这样整个Spring Boot项目就正式启动完成了。与此同时,经过初始化封装设置的应用上下文ApplicationContext也处于活跃状态。
至此,关于Spring Boot执行流程中项目的初始化启动已经分析完毕。经过上面对项目启动过程中两阶段源码的详细分析,相信大家对Spring Boot执行流程已经有了大体的认识,虽然大部分内容都是较为复杂,但在学习过程中只要了解源码中部分重要内容即可。
下面,通过一个Spring Boot执行流程图,让大家更清晰的知道Spring Boot的整体执行流程和主要启动阶段,具体如图1所示。
图1 Spring Boot的执行流程示意图
需要说明的是,图1所示的Spring Boot执行流程是基于本教材编写时的最新稳定版本Spring Boot 2.1.3为例进行分析讲解的,不同的版本内部执行过程和源码实现细节略有不同,大家要学会借助源码分析对应版本程序的执行流程。