这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战
前言
之前通过一个简短的系列更文,让我们认识了Spring Boot的自动装配,也实现了自己的starter,可以说对Spring Boot也有了一定了解。等等,你真的了解Spring Boot了么,那你知道Spring Boot是什么启动的吗?这。。。。不是点击run就启动了吗?
下面我们真正认识下Spring Boot的启动过程吧,顺便放下之前系列更文的链接,没看的小伙伴可以看看
- Spring Boot 回顾(五):实现第一个自定义starter
- Spring Boot 回顾(四):深入理解SpringFactoriesLoader
- Spring Boot 回顾(三):自动装配原理解析
- Spring Boot 回顾(二):多模块下bean的注入
- Spring Boot 回顾(一):实现自己的第一个自定义注解
run方法详解
首先看下之前项目中的启动类
我们注意到其中的main方法,里面都有这样一段代码
SpringApplication.run(ClientApplication.class, args);
基本上可以猜测出Spring Boot项目的启动肯定是从这里开始的,那么我们就沿着这里看下去吧。我们使用的是SpringApplication的静态run方法,那么,这个方法里面首先要创建一个SpringApplication对象实例,然后调用这个创建好的SpringApplication的实例方法。
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
return new SpringApplication(sources).run(args);
}
同时,在创建SpringApplication对象实例时,会调用它自己的initialize方法
private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
this.webEnvironment = deduceWebEnvironment();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
初始化initialize方法执行完之后,开始进入Spring BooT启动流程的重点,这时开始调用我们的run方法,开始启动SpringBoot。源码如下
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
ConfigurableApplicationContext context = null;
this.configureHeadlessProperty();
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
this.configureIgnoreBeanInfo(environment);
Banner printedBanner = this.printBanner(environment);
context = this.createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
this.prepareContext(bootstrapContext, 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);
}
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
整个方法还是比较长的,我们耐心看下去。首先开启我们的StopWatch定时器,用来记录任务的执行时间;接下来调用createBootstrapContext,它主要是初始化启动上下文;接着configureHeadlessProperty进行系统参数的设置;然后是初始化监听器列表,调用getRunListeners;监听器列表启动完成后,再发布springboot开始启动事件;后面在整个try代码块中,则主要进行spring上下文环境的准备,其中包括设置环境变量、加载ApplicationContextInitializer、完成相应bean的注册等操作。最后会通知所有listener,Spring容器启动完成
总结
最后,我们用流程图画出整个run方法的流程
graph TD
Start --> 创建并准备环境参数--> 创建并初始化ApplicationContext--> 调用ApplicationContext的refresh方法,完成启动--> Stop