Spring Boot应用的启动过程看似简单,但其中涉及了复杂的初始化和加载机制。本文将深入剖析Spring Boot的启动流程,了解其自动配置、引导启动和源码运行等 every detail。
一、整体启动流程
当我们通过java -jar
命令启动Spring Boot应用时,整个启动过程经历了以下关键步骤:
- 装载核心启动器类:
org.springframework.boot.SpringApplication
- 运行
SpringApplication
的静态run
方法,传入主配置类 - 实例化
SpringApplication
对象,加载应用上下文初始化器 - 准备并刷新应用上下文
Context
- 触发所有
CommandLineRunner
执行 - 启动完成,等待退出
接下来我们重点看一下启动的源码流程和自动配置机制。
二、SpringApplication启动流程剖析
SpringApplication
类提供了一站式服务来引导启动整个Spring Boot程序,其中封装了很多启动时的初始化和加载逻辑。
1. 初始化启动类
通过构造函数创建SpringApplication实例时,进行了一系列的初始化工作:
- 判断并设置web环境类型:
SERVLET
、REACTIVE
等 - 使用
SpringFactoriesLoader
加载ApplicationContextInitializer
和ApplicationListener
- 推断并设置主配置类
primary sources
public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
...
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.setInitializers(this.getSpringFactoriesInstances();
this.setListeners(this.getSpringFactoriesInstances());
this.mainApplicationClass = this.deduceMainApplicationClass();
}
这样就创建好了一个可执行的SpringApplication
实例。
2. 开始执行run方法
接着调用run(args)
方法启动整个Spring Boot程序:
public ConfigurableApplicationContext run(String... args) {
// 停止watch服务
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 声明应用上下文和异常报告集合
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters;
// 头部打印Banner
configureHeadlessProperty();
// 获取SpringApplicationRunListeners,用于监听run方法的整个流程
SpringApplicationRunListeners listeners = getRunListeners(args);
// 启动所有的监听器
listeners.starting();
try {
// 封装命令行参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
// 准备环境 Environment
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
......
}
catch (Throwable ex) {
......
}
// 完成时长记录
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
// 发布应用启动完成事件
listeners.started(context);
// 执行所有 runners
callRunners(context, applicationArguments);
}
run
方法主要完成了以下工作:
- 构建应用启动监听器
SpringApplicationRunListeners
- 封装并准备环境
Environment
- 创建并配置应用上下文
ApplicationContext
- 回调
ApplicationContext
初始化器initializers
- 最后一步
refresh
应用上下文使其完成加载
在这段代码中,我们可以看到启动的关键步骤都出现了,包括监听器、环境、应用上下文的准备,其中隐含了复杂的加载机制。
3. 创建应用上下文
SpringApplication
会根据web环境类型创建响应的应用上下文对象,常见的两种:
AnnotationConfigServletWebServerApplicationContext
:web环境AnnotationConfigApplicationContext
:普通应用
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
//利用反射实例化上下文
context = (ConfigurableApplicationContext)
contextClass.getConstructor(ApplicationContext.class)
.newInstance(this.applicationContext);
可以看到Spring Boot会通过应用类型选择合适的ApplicationContext
来实例化,保证后续组件的加载顺利进行。
4. 准备应用上下文
在获得ApplicationContext
实例后,Spring Boot会继续对其进行准备工作,主要在 prepareContext()
方法中:
- 将命令行参数添加到 Environment 中
- 应用
ApplicationContextInitializer
初始化器到上下文 - 加载主配置类信息
primarySources
到上下文 - 触发监听器的
contextPrepared
事件
这样一系列的准备工作完成了对上下文环境的构建和初始化,为后续的 refresh
提供基础。
5. 刷新应用上下文
最后通过调用 refresh()
方法启动整个应用上下文:
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
}
}
}
public void refresh() throws BeansException, IllegalStateException {
// 初始化剩余的bean定义
finishBeanFactoryInitialization(beanFactory);
// 最后一步:发布容器刷新完成事件
finishRefresh();
}
在这里,容器会逐步完成Bean定义的加载、注入依赖、初始化等工作,是启动过程中最关键的部分。
至此,SpringApplication
的执行流程我们就解析完毕了,可以看到其包含了初始化、监听、载入、刷新等多个关键步骤,将应用启动的复杂过程进行了串联和统一。这就是Spring Boot应用启动的整体流程。
三、自动配置流程解析
除了启动流程,Spring Boot中还包含了强大的自动配置功能,这也是其魅力所在。那么Spring Boot又是如何实现自动配置的呢?
1. @EnableAutoConfiguration
在Spring Boot主配置类上,通常会通过 @EnableAutoConfiguration
注解开启自动配置:
@Configuration
@EnableAutoConfiguration
public class MyApplication { }
这里的 @EnableAutoConfiguration
实现了自动配置的开关。
2. AutoConfigurationImportSelector
@EnableAutoConfiguration
的关键在于向容器导入了一个 AutoConfigurationImportSelector
的组件,它实现了 DeferredImportSelector
接口。
在其中 getAutoConfigurationEntry()
方法包含了自动配置的核心;
3. 加载自动配置类
AutoConfigurationImportSelector
通过getAutoConfigurationEntry()
方法加载自动配置类:
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
// 获取所有自动配置类
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 去重并过滤
configurations = removeDuplicates(configurations);
// 封装并返回配置类
return new AutoConfigurationEntry(configurations, exclusions);
}
这里主要通过Spring Factories Loader机制加载META-INF/spring.factories
中的自动配置类实现EnableAutoConfiguration。
4. 自动配置类生效
上一步加载了大量的自动配置类,但并不是所有配置都会生效,这就涉及到了条件装配。
每一个自动配置类中都定义了 @Conditional
条件注解,只有当条件匹配才会将配置添加到上下文中。
例如 @ConditionalOnClass
指定的类必须存在,@ConditionalOnMissingBean
对应类型的Bean必须不存在等。
这些条件实现了自动配置的精确控制,避免引入不必要的组件。
代码语言:javascript复制@Configuration
@ConditionalOnClass(DataSource.class)
public class DataSourceAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public DataSource dataSource() {
// ...
}
}
Spring Boot通过条件装配实现了自动配置的优雅适配。
总结
至此,我们剖析完了Spring Boot应用启动和自动配置的整个过程,可以看到其内部对容器上下文环境的构建进行了精心设计,使得应用能够顺利启动并加载所需的Bean。这种自动配置编程模型是Spring Boot最耀眼的设计之一。