前言
SpringBoot启动流程总体分为两步,第一步先SpringApplication对象,第二步为执行run()方法。我们只要抓住这个思路,在深入研究每一步背后做了什么,对SpringBoot的启动流程就能熟练掌握。
一、创建SpringApplication对象
1.首先进入run()方法,run方法中new创建了一个SpringApplication实例。
2.下面是创建SpringApplication实例所做的一系列操作。
总结:SpringApplication实例所做的一系列操作有以下几个关键步骤:
- SpringApplication对象在初始化时,从类路径中推断并设置应用类型。应用类型有三种:非web应用,基于servlet的web应用和基于REACTIVE的web应用。
- 从SpringFactories文件中找到配置的事件监听器,并保存起来
- 推断运行主类
二、执行 run() 方法
创建完SpringApplication实例之后,继续执行run方法,run方法的源码如下。
public ConfigurableApplicationContext run(String... args) {
//1.开启程序启动计时器
long startTime = System.nanoTime();
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
//2.无输入不影响工作
configureHeadlessProperty();
//3.获取监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
//4.启动监听器
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
//5.设置程序启动参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//6.配置环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
//7.跳过bean检索
configureIgnoreBeanInfo(environment);
//8.打印banner
Banner printedBanner = printBanner(environment);
//9.创建应用程序上下文
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
//10.刷新上下文
refreshContext(context);
//11.上下文后置处理
afterRefresh(context, applicationArguments);
//12.结束计时器
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
//13.启动监听器
listeners.started(context, timeTakenToStartup);
//14.通知程序运行
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
listeners.ready(context, timeTakenToReady);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
该方法中实现了如下几个关键步骤:
- 创建了应用的监听器SpringApplicationRunListeners并开始监听。
- 加载SpringBoot配置环境(ConfigurableEnvironment),如果是通过web容器发布,会加载StandardEnvironment,其最终也是继承了ConfigurableEnvironment。
- 创建run方法的返回对象:ConfigurableApplicationContext(应用配置上下文)。ConfigurableApplicationContext是ApplicationContext的子类,该接口的主要任务就是配置应用上下文功能。
- 回到run方法内,prepareContext方法将listeners、environment、applicationArguments、banner等重要组件与上下文对象关联。
- 接下来的refreshContext(context)方法是实现spring-boot-starter-*(mybatis、redis等)自动化配置的关键,包括spring.factories的加载,bean的实例化等核心工作。
- 启动监听器,通知程序运行。
总结:Springboot的启动,主要创建了配置环境(environment)、事件监听(listeners)、应用上下文(applicationContext),并基于以上条件,在容器中开始实例化我们需要的Bean。
总结
SpringApplication类为我们引导项目提供了一种便利的方式——通过main()方法直接启动。大多数情况下,我们可以把项目启动这个任务直接委托给SpringApplication.run方法。所以我们在学习SpringBoot启动流程时,抓住两点即可:1.创建并初始化SpringApplication ;2.执行run()方法。