Spring Boot 启动流程深度解析:从run()到自动配置
- 前言 - 引入主题
- 启动流程概览 - 宏观视角
- ApplicationContext初始化 - 核心组件
- prepareContext详解 - 准备阶段
- refreshContext流程 - 刷新机制
- 自动配置原理 - 深度剖析
- 实践验证 - 代码示例
- 时序图 - 可视化流程
- 源码解析 - 核心代码
- 常见问题 - 实战经验
- 总结 - 知识提炼
前言
Spring Boot作为现代Java开发的事实标准,其"约定优于配置"的设计理念大大简化了应用开发。但当我们运行SpringApplication.run()时,底层究竟发生了什么?本文将深入剖析Spring Boot的启动流程,揭示自动配置的奥秘。
一、启动流程概览
1.1 核心入口
java
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
看似简单的一行代码,背后却经历了复杂而精密的初始化过程。
1.2 启动流程关键步骤
Spring Boot的启动主要分为以下几个阶段:
- 创建SpringApplication实例
- 准备Environment(环境)
- 创建ApplicationContext(应用上下文)
- 准备Context(prepareContext)
- 刷新Context(refreshContext)
- 完成启动
二、深入核心:ApplicationContext的初始化
2.1 ApplicationContextInitializer接口
在Spring Boot启动过程中,ApplicationContextInitializer扮演着重要角色:
java
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
void initialize(C applicationContext);
}
作用: 在ApplicationContext刷新之前进行自定义初始化,比如添加BeanFactoryPostProcessor、注册监听器等。
2.2 创建ApplicationContext
第二步是创建ApplicationContext实例,根据应用类型(Servlet、Reactive、None)创建对应的上下文:
java
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
switch (this.webApplicationType) {
case SERVLET:
contextClass = AnnotationConfigServletWebServerApplicationContext.class;
break;
case REACTIVE:
contextClass = AnnotationConfigReactiveWebServerApplicationContext.class;
break;
default:
contextClass = AnnotationConfigApplicationContext.class;
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
三、prepareContext()方法详解
prepareContext()方法是启动流程中的关键环节,它完成了以下核心任务:
3.1 设置环境
java
context.setEnvironment(environment);
将准备好的Environment设置到ApplicationContext中。
3.2 应用初始化器
java
applyInitializers(context);
调用所有ApplicationContextInitializer的initialize()方法。
3.3 加载Bean定义
通过load()方法加载主配置类及其他配置:
java
protected void load(ApplicationContext context, Object[] sources) {
BeanDefinitionLoader loader = createBeanDefinitionLoader(
getBeanDefinitionRegistry(context), sources);
loader.load();
}
这一步会:
- 注解扫描(@ComponentScan)
- 导入配置类(@Import)
- 加载XML配置
- 注册主配置类为BeanDefinition
四、refreshContext():核心刷新流程
refreshContext()调用AbstractApplicationContext的refresh()方法,这是Spring容器启动的核心:
4.1 BeanDefinition注册
在invokeBeanFactoryPostProcessors()阶段:
java
@Override
public void refresh() throws BeansException {
// ...
// 调用BeanFactory后处理器
invokeBeanFactoryPostProcessors(beanFactory);
// 注册Bean后处理器
registerBeanPostProcessors(beanFactory);
// 初始化消息源
initMessageSource();
// 初始化事件多播器
initApplicationEventMulticaster();
// 刷新特定上下文子类
onRefresh();
// 注册监听器
registerListeners();
// 实例化所有非懒加载的单例Bean
finishBeanFactoryInitialization(beanFactory);
// 完成刷新
finishRefresh();
}
4.2 自动配置的加载
在这个阶段,Spring Boot会:
- 读取spring.factories :加载
META-INF/spring.factories中配置的自动配置类 - 条件评估 :根据
@Conditional注解判断是否需要加载 - Bean实例化:创建并注册符合条件的Bean
- 属性绑定:将配置文件中的属性绑定到Bean
五、自动配置原理深度剖析
5.1 @SpringBootApplication注解
java
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public @interface SpringBootApplication {
}
5.2 @EnableAutoConfiguration
java
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}
AutoConfigurationImportSelector会:
- 从
spring.factories加载自动配置类 - 通过
@Conditional系列注解进行条件判断 - 排除不需要的配置
- 返回需要导入的配置类全限定名
5.3 条件注解示例
java
@Configuration
@ConditionalOnClass(DataSource.class)
@ConditionalOnMissingBean(DataSource.class)
public class DataSourceAutoConfiguration {
// 自动配置DataSource
}
六、实践验证:自定义Initializer
6.1 创建自定义Initializer
java
public class MyApplicationContextInitializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext context) {
System.out.println("=== 自定义初始化器执行 ===");
// 注册自定义BeanDefinition
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) context.getBeanFactory();
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(MyBean.class);
registry.registerBeanDefinition("myBean", beanDefinition);
}
}
6.2 注册方式
方式一:spring.factories
在META-INF/spring.factories中配置:
properties
org.springframework.context.ApplicationContextInitializer=\
com.example.MyApplicationContextInitializer
方式二:编程方式
java
SpringApplication app = new SpringApplication(Application.class);
app.addInitializers(new MyApplicationContextInitializer());
app.run(args);
七、启动流程时序图
用户代码
↓
SpringApplication.run()
↓
创建SpringApplication实例
↓
准备Environment
↓
创建ApplicationContext
↓
prepareContext()
├── 设置Environment
├── 应用Initializers
├── 发布事件
└── 加载Bean定义
↓
refreshContext()
├── prepareBeanFactory()
├── invokeBeanFactoryPostProcessors()
│ └── 处理自动配置类
├── registerBeanPostProcessors()
├── finishBeanFactoryInitialization()
│ └── 实例化Bean
└── finishRefresh()
└── 发布启动完成事件
↓
启动完成
八、核心源码解析
8.1 SpringApplication.run()核心代码
java
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 1. 准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 2. 打印Banner
Banner printedBanner = printBanner(environment);
// 3. 创建ApplicationContext
context = createApplicationContext();
// 4. 准备上下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 5. 刷新上下文(核心)
refreshContext(context);
// 6. 刷新后处理
afterRefresh(context, applicationArguments);
stopWatch.stop();
return context;
}
8.2 自动配置加载源码
java
public class AutoConfigurationImportSelector {
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
return configurations;
}
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
}
九、常见问题与最佳实践
9.1 Bean定义冲突
问题: 多个自动配置类尝试注册同名Bean
解决: 使用@ConditionalOnMissingBean确保只在Bean不存在时才注册
9.2 循环依赖
问题: Bean之间存在循环依赖导致启动失败
解决:
- 重构代码,消除循环依赖
- 使用
@Lazy延迟加载 - 使用Setter注入代替构造器注入
9.3 自动配置不生效
排查步骤:
- 检查
spring.factories配置是否正确 - 验证
@Conditional条件是否满足 - 使用
--debug启动查看自动配置报告 - 检查是否被
exclude排除
9.4 启动速度优化
优化建议:
- 排除不需要的自动配置
- 使用Lazy Initialization(Spring Boot 2.2+)
- 合理使用
@ComponentScan范围 - 考虑使用GraalVM Native Image
十、总结
Spring Boot的启动流程体现了精妙的设计:
- 分层清晰:从Environment准备到Context创建,再到Bean初始化,层次分明
- 扩展性强:通过Initializer、Listener、BeanPostProcessor等提供多个扩展点
- 自动化:自动配置机制大幅降低了配置复杂度
- 条件化:@Conditional系列注解实现灵活的配置加载
理解启动流程不仅有助于排查问题,更能帮助我们更好地利用Spring Boot的扩展机制,构建更加优雅和高效的应用。
参考资源
- Spring Boot官方文档:https://spring.io/projects/spring-boot
- Spring Framework源码:https://github.com/spring-projects/spring-framework
- Spring Boot源码:https://github.com/spring-projects/spring-boot
💡 提示: 建议结合源码调试,逐步跟踪启动流程,加深理解。