1. Spring Boot启动原理深度解析
作为一名长期使用Spring Boot框架的后端开发者,我经常被问到Spring Boot的启动机制。今天,我将从源码层面详细剖析Spring Boot的启动过程,帮助大家理解这个强大框架背后的工作原理。
Spring Boot的启动看似简单,只需一个main方法和@SpringBootApplication注解,但背后隐藏着复杂的初始化流程。理解这些机制不仅能帮助我们更好地使用Spring Boot,还能在遇到问题时快速定位原因。
2. Spring Boot应用启动入口
2.1 基础启动代码
典型的Spring Boot应用启动代码如下:
java复制@SpringBootApplication
@Slf4j
public class ApplicationMain {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(ApplicationMain.class, args);
}
}
这段简洁的代码背后,Spring Boot完成了大量工作。@SpringBootApplication是一个复合注解,包含@Configuration、@EnableAutoConfiguration和@ComponentScan三个核心注解。
2.2 SpringApplication的职责
SpringApplication类是整个启动过程的核心,它主要完成以下工作:
- 创建适当的ApplicationContext实例(根据类路径决定)
- 注册CommandLinePropertySource以将命令行参数暴露为Spring属性
- 刷新应用上下文,加载所有单例bean
- 触发所有CommandLineRunner bean的执行
3. SpringApplication初始化阶段
3.1 构造函数分析
启动过程首先创建SpringApplication实例,然后执行run方法:
java复制public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
构造函数中完成了关键初始化工作:
java复制this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
3.2 初始化关键步骤
- 应用类型推断:通过检查类路径中是否存在特定类来判断是Web应用还是普通应用
- 初始化ApplicationContextInitializer:从spring.factories加载并设置,这些初始化器在refresh()前被调用
- 初始化ApplicationListener:同样从spring.factories加载,用于监听各种应用事件
- 推断主启动类:通过分析调用栈找出包含main方法的类
4. run()方法执行流程
4.1 run()方法概览
run()方法是启动过程的核心,主要执行以下步骤:
java复制configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
4.2 关键步骤详解
- 配置headless模式:设置java.awt.headless属性,用于无显示设备的环境
- 获取运行监听器:从spring.factories加载SpringApplicationRunListener实现
- 准备环境:创建和配置环境,包括处理命令行参数
- 创建应用上下文:根据webApplicationType创建适当的ApplicationContext
- 准备上下文:设置环境、后处理bean定义等
- 刷新上下文:核心步骤,完成bean的加载和初始化
- 调用Runner:执行ApplicationRunner和CommandLineRunner实现
5. 环境准备与环境配置
5.1 prepareEnvironment方法
java复制private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
5.2 环境处理关键点
- 环境创建:根据应用类型创建StandardEnvironment或StandardServletEnvironment
- 配置环境:处理命令行参数并添加到环境属性中
- 属性绑定:将环境中的spring.main.*属性绑定到SpringApplication
- 配置属性源:附加动态跟踪底层环境属性源的解析器
6. 应用上下文准备与刷新
6.1 prepareContext方法
java复制private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
6.2 refreshContext方法
refreshContext是Spring容器初始化的核心,主要完成:
- 准备刷新:设置启动时间、活跃状态,初始化属性源
- 获取BeanFactory:创建或刷新内部的BeanFactory
- 准备BeanFactory:设置类加载器、表达式解析器等
- 执行BeanFactory后置处理器:处理@Configuration类等
- 注册Bean后置处理器:用于bean初始化前后的处理
- 初始化消息源:国际化支持
- 初始化事件广播器:用于应用事件发布
- 完成BeanFactory初始化:实例化所有非懒加载的单例bean
7. 自动配置原理
7.1 @EnableAutoConfiguration
@SpringBootApplication中的@EnableAutoConfiguration是自动配置的关键,它:
- 通过@Import(AutoConfigurationImportSelector.class)导入自动配置选择器
- AutoConfigurationImportSelector从META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports加载自动配置类
- 这些配置类根据条件(@Conditional)决定是否生效
7.2 自动配置条件
Spring Boot提供了丰富的条件注解:
- @ConditionalOnClass:类路径存在指定类时生效
- @ConditionalOnMissingBean:容器中不存在指定Bean时生效
- @ConditionalOnProperty:配置属性满足条件时生效
- @ConditionalOnWebApplication:Web应用时生效
8. 启动扩展点
Spring Boot提供了多个扩展点,允许我们在启动过程的不同阶段插入自定义逻辑:
8.1 ApplicationContextInitializer
在ApplicationContext刷新前执行,用于对上下文进行编程式配置:
java复制public class MyInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
// 初始化逻辑
}
}
需要在META-INF/spring.factories中注册:
code复制org.springframework.context.ApplicationContextInitializer=com.example.MyInitializer
8.2 ApplicationListener
监听各种应用事件,如ContextRefreshedEvent、ApplicationStartedEvent等:
java复制public class MyListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// 事件处理逻辑
}
}
8.3 CommandLineRunner/ApplicationRunner
在应用完全启动后执行,适合执行一些初始化任务:
java复制@Component
public class MyRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
// 启动后逻辑
}
}
9. 常见问题与解决方案
9.1 启动缓慢问题
可能原因及解决方案:
- 类路径扫描过多:使用@ComponentScan的basePackages限定扫描范围
- 自动配置类过多:通过spring.autoconfigure.exclude排除不必要的自动配置
- Bean初始化耗时:检查@PostConstruct方法和InitializingBean的实现
9.2 自动配置不生效
排查步骤:
- 检查是否缺少必要的starter依赖
- 查看自动配置报告:启动时添加--debug参数
- 检查是否有自定义Bean覆盖了自动配置
9.3 循环依赖问题
解决方案:
- 重构代码,避免循环依赖
- 使用@Lazy延迟初始化
- 使用setter注入代替构造器注入
10. 性能优化建议
10.1 启动优化
- 使用Spring Boot 2.4+:新版在启动性能上有显著提升
- 延迟初始化:设置spring.main.lazy-initialization=true
- 精简依赖:移除不必要的starter
- 使用AOT:Spring Native支持提前编译
10.2 内存优化
- 合理设置JVM参数:-Xms和-Xmx
- 监控内存使用:使用Actuator的/metrics端点
- 避免内存泄漏:注意静态集合、ThreadLocal的使用
11. 实际应用案例
11.1 自定义Banner
在resources目录下添加banner.txt:
code复制 . ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _
( ( )\___ | '_ | '_| | '_ \/ _` |
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v${spring-boot.version})
11.2 自定义健康检查
实现HealthIndicator接口:
java复制@Component
public class MyHealthIndicator implements HealthIndicator {
@Override
public Health health() {
// 自定义健康检查逻辑
return Health.up().withDetail("detail", "value").build();
}
}
12. 启动流程全景图
Spring Boot启动过程可以概括为以下几个主要阶段:
- 初始化阶段:创建SpringApplication实例,加载Initializer和Listener
- 环境准备阶段:创建和配置Environment
- 上下文创建阶段:根据应用类型创建ApplicationContext
- 准备上下文阶段:加载bean定义,应用Initializer
- 刷新上下文阶段:完成bean的实例化和初始化
- 后处理阶段:执行Runner,完成启动
理解这些阶段及其关键操作,有助于我们更好地掌握Spring Boot的工作原理,在开发中更加得心应手。