开发任何基于 Spring Boot 的项目,我们都会使用以下的启动类:
首先在idea中构建一个全新的springboot程序
大致的启动类长这样:
@SpringBootApplication
public class Demo1Application {
public static void main(String[] args) {
//1、new了一个SpringApplication对象,使用SPI技术加载spring.factories文件中ApplicationContextInitializer、ApplicationListener 接口实例
//2.调用SpringApplication.run() 方法
SpringApplication.run(Demo1Application.class, args);
}
}
可以看到,Demo1Application 类中定义了注解 @SpringBootApplication,main 方法里通过 SpringApplication.run 来启动整个应用程序。因此要研究 Spring Boot 的启动原理,我们就需要从这两个地方入手。首先我们研究这个@SpringBootApplication这个注解,实际上这个注解是3个注解的联合,分别是
@SpringBootConfiguration 用于标识当前类是一个配置类,所以我们在启动类中也可以写入一些配置,其实就是一个 Configuration,但是 Spring Boot 推荐用 SpringBootConfiguration 来代替 Configuration。
@EnableAutoConfiguration 可以帮助SpringBoot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器。
@ComponentScan 根据定义的扫描路径,把符合扫描规则的类装配到spring容器中,可以通过 basePakcages 来指定其扫描的范围,如果不指定,则默认从标注了 @ComponentScan 注解的类所在包开始扫描。
每一个SpringBoot程序都有一个主入口,这个主入口就是main方法,而main方法中都会调用SpringBootApplication.run方法,一个快速了解SpringBootApplication启动过程的好方法就是在run方法中打一个断点,然后通过Debug的模式启动工程,逐步跟踪了解SpringBoot源码是如何完成环境准备和启动加载bean的。接下来我们跟着run方法研究他的执行流程,一直点击run方法,直到出现以下代码块
public ConfigurableApplicationContext run(String... args) {
//记录任务的执行时间
long startTime = System.nanoTime();
// 创建引导上下文
DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
// 定义可配置的应用程序上下文变量
ConfigurableApplicationContext context = null;
this.configureHeadlessProperty();
// 获取运行监听器
SpringApplicationRunListeners listeners = this.getRunListeners(args);
// 启动监听器
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 默认应用程序参数,将args封装为 ApplicationArguments 对象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//接着prepareEnvironment根据运行监听器和参数准备spring环境
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
this.configureIgnoreBeanInfo(environment);
Banner printedBanner = this.printBanner(environment);
//接着调用createApplicationContext方法创建应用上下文
context = this.createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
//通过prepareContext准备应用上下文
this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
//调用refreshContext() 方法启动Spring容器和内置的Servlet容器
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);
}
listeners.started(context, timeTakenToStartup);
//callRunners方法执行所有runner运行器
this.callRunners(context, applicationArguments);
} catch (Throwable var12) {
this.handleRunFailure(context, var12, listeners);
throw new IllegalStateException(var12);
}
try {
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
listeners.ready(context, timeTakenToReady);
return context;
} catch (Throwable var11) {
this.handleRunFailure(context, var11, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var11);
}
}
最后我们来进行总结
-
记录任务的执行时间
-
创建引导上下文
-
定义可配置的应用程序上下文变量
-
获取运行监听器
-
启动监听器
-
默认应用程序参数,将args封装为 ApplicationArguments 对象
-
接着prepareEnvironment根据运行监听器和参数准备spring环境
-
接着调用createApplicationContext方法创建应用上下文
-
通过prepareContext准备应用上下文
-
调用refreshContext() 方法启动Spring容器和内置的Servlet容器
-
callRunners方法执行所有runner运行器