1. Spring Boot启动流程深度解析
作为一名长期使用Spring Boot框架的后端开发者,我经常被问到Spring Boot应用启动时究竟发生了什么。今天,我将从源码层面详细剖析Spring Boot的启动机制,帮助大家理解这个"黑盒子"内部的工作原理。
Spring Boot的启动过程可以概括为两个核心阶段:SpringApplication实例化和run()方法执行。我们先从一个典型的启动类开始:
java复制@SpringBootApplication
@Slf4j
public class ApplicationMain {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(ApplicationMain.class, args);
}
}
这段简单的代码背后,Spring Boot完成了大量复杂的工作。让我们深入源码,一步步拆解这个过程。
2. SpringApplication初始化过程
2.1 构造函数核心逻辑
启动过程首先会创建SpringApplication实例,这是整个启动流程的起点:
java复制public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
构造函数中完成了几个关键操作:
java复制this.resourceLoader = resourceLoader;
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 1. 推断Web应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 2. 加载ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 3. 加载ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 4. 推断主启动类
this.mainApplicationClass = deduceMainApplicationClass();
这里有几个关键点值得注意:
-
Web应用类型推断:Spring Boot会检查类路径下是否存在特定的类来判断应用类型(Servlet、Reactive或非Web应用)。这个判断直接影响后续ApplicationContext的类型选择。
-
工厂加载机制:
getSpringFactoriesInstances方法通过spring.factories文件加载扩展组件,这是Spring Boot自动配置的核心机制之一。 -
启动类推断:通过分析调用栈来定位主启动类,这种方法虽然巧妙但也可能导致在某些特殊场景下识别失败。
2.2 应用类型推断细节
Web应用类型推断是通过检查类路径中是否存在特定类来完成的:
java复制static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
这种设计使得Spring Boot能够自动适应不同类型的应用,无需手动配置。
3. run()方法执行流程
3.1 run()方法整体结构
run()方法是Spring Boot启动的核心,其基本结构如下:
java复制public ConfigurableApplicationContext run(String... args) {
// 1. 计时开始
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 2. 准备环境
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
// 3. 创建应用参数和环境
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 4. 打印Banner
Banner printedBanner = printBanner(environment);
// 5. 创建应用上下文
context = createApplicationContext();
// 6. 准备上下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 7. 刷新上下文
refreshContext(context);
// 8. 执行Runner
callRunners(context, applicationArguments);
// 9. 完成启动
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
return context;
}
catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
}
3.2 环境准备阶段
环境准备是启动过程中的重要环节:
java复制private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 创建环境对象
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置环境
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 发布环境准备事件
listeners.environmentPrepared(environment);
// 绑定环境到SpringApplication
bindToSpringApplication(environment);
// 环境转换
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
// 附加配置属性源
ConfigurationPropertySources.attach(environment);
return environment;
}
这个阶段有几个关键操作:
-
环境创建:根据应用类型创建对应的环境对象(StandardEnvironment、StandardServletEnvironment等)
-
属性绑定:将命令行参数绑定到环境属性中
-
配置属性处理:处理
application.properties和application.yml等配置文件
3.3 上下文准备阶段
上下文准备阶段负责初始化ApplicationContext:
java复制private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
// 设置环境
context.setEnvironment(environment);
// 后处理应用上下文
postProcessApplicationContext(context);
// 应用初始化器
applyInitializers(context);
// 发布上下文准备事件
listeners.contextPrepared(context);
// 注册单例bean
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
// 加载源
Set<Object> sources = getAllSources();
load(context, sources.toArray(new Object[0]));
// 发布上下文加载事件
listeners.contextLoaded(context);
}
这个阶段完成了BeanDefinition的加载和注册,为后续的Bean实例化奠定了基础。
4. 容器刷新过程
4.1 refresh()方法解析
容器刷新是Spring Boot启动过程中最复杂的部分,核心逻辑在AbstractApplicationContext.refresh()方法中:
java复制public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 1. 准备刷新
prepareRefresh();
// 2. 获取BeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 3. 准备BeanFactory
prepareBeanFactory(beanFactory);
try {
// 4. 后处理BeanFactory
postProcessBeanFactory(beanFactory);
// 5. 调用BeanFactory后置处理器
invokeBeanFactoryPostProcessors(beanFactory);
// 6. 注册Bean后置处理器
registerBeanPostProcessors(beanFactory);
// 7. 初始化消息源
initMessageSource();
// 8. 初始化事件广播器
initApplicationEventMulticaster();
// 9. 模板方法,子类实现
onRefresh();
// 10. 注册监听器
registerListeners();
// 11. 完成BeanFactory初始化
finishBeanFactoryInitialization(beanFactory);
// 12. 完成刷新
finishRefresh();
}
catch (BeansException ex) {
// 异常处理
destroyBeans();
cancelRefresh(ex);
throw ex;
}
finally {
// 13. 重置缓存
resetCommonCaches();
}
}
}
4.2 关键步骤详解
4.2.1 invokeBeanFactoryPostProcessors
这个方法负责执行所有的BeanFactoryPostProcessor,是自动配置的核心:
java复制protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(
beanFactory, getBeanFactoryPostProcessors());
if (beanFactory.getTempClassLoader() == null
&& beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
}
其中最重要的是ConfigurationClassPostProcessor,它负责处理@Configuration类:
- 解析@ComponentScan注解,扫描指定包下的组件
- 解析@Import注解,导入其他配置类
- 解析@Bean方法,注册BeanDefinition
4.2.2 finishBeanFactoryInitialization
这个方法完成了所有非懒加载单例Bean的实例化:
java复制protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// 初始化ConversionService
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
// 注册默认的嵌入值解析器
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
}
// 初始化LoadTimeWeaverAware Bean
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// 停止使用临时类加载器
beanFactory.setTempClassLoader(null);
// 冻结所有BeanDefinition
beanFactory.freezeConfiguration();
// 实例化所有剩余的单例Bean
beanFactory.preInstantiateSingletons();
}
5. 启动扩展点
Spring Boot提供了多个扩展点,允许开发者在启动过程的不同阶段插入自定义逻辑:
5.1 ApplicationContextInitializer
在ApplicationContext刷新前执行,用于对ConfigurableApplicationContext进行自定义操作:
java复制public class CustomInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
// 自定义初始化逻辑
}
}
需要在META-INF/spring.factories中注册:
code复制org.springframework.context.ApplicationContextInitializer=com.example.CustomInitializer
5.2 ApplicationListener
监听Spring应用事件,如ContextRefreshedEvent、ApplicationStartedEvent等:
java复制public class CustomListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// 处理事件
}
}
5.3 SpringApplicationRunListener
监听整个启动过程,提供了多个回调方法:
java复制public interface SpringApplicationRunListener {
default void starting() {}
default void environmentPrepared(ConfigurableEnvironment environment) {}
default void contextPrepared(ConfigurableApplicationContext context) {}
default void contextLoaded(ConfigurableApplicationContext context) {}
default void started(ConfigurableApplicationContext context) {}
default void running(ConfigurableApplicationContext context) {}
default void failed(ConfigurableApplicationContext context, Throwable exception) {}
}
5.4 CommandLineRunner和ApplicationRunner
在应用启动完成后执行,适合执行一些初始化任务:
java复制@Component
@Order(1)
public class CustomRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
// 启动后执行的任务
}
}
两者的区别在于ApplicationRunner接收封装后的应用参数(ApplicationArguments),而CommandLineRunner直接接收原始字符串数组。
6. 启动流程优化与问题排查
6.1 启动性能优化
-
组件扫描优化:
- 使用明确的@ComponentScan路径,避免全包扫描
- 使用@Filter排除不必要的组件
-
延迟初始化:
properties复制spring.main.lazy-initialization=true -
JVM参数调优:
- 适当增加初始堆内存(-Xms)和最大堆内存(-Xmx)
- 使用-XX:TieredStopAtLevel=1限制JIT编译级别
6.2 常见问题排查
-
Bean创建失败:
- 检查依赖注入是否正确
- 查看是否有循环依赖
- 检查Bean的作用域是否匹配
-
自动配置失效:
- 检查是否添加了必要的starter依赖
- 检查是否有@Conditional条件不满足
- 查看自动配置报告(debug模式下)
-
启动超时:
- 检查是否有长时间阻塞的初始化任务
- 考虑使用@Async异步执行耗时任务
7. 启动流程全景图
为了更直观地理解整个启动过程,我整理了一个简化的流程图:
-
初始化阶段:
- 创建SpringApplication实例
- 推断Web应用类型
- 加载ApplicationContextInitializer和ApplicationListener
-
环境准备阶段:
- 创建和配置Environment
- 加载配置文件
- 发布环境准备事件
-
上下文创建阶段:
- 创建ApplicationContext
- 准备上下文
- 加载BeanDefinition
-
容器刷新阶段:
- 调用BeanFactoryPostProcessor
- 注册BeanPostProcessor
- 初始化单例Bean
-
启动完成阶段:
- 执行Runner
- 发布启动完成事件
理解Spring Boot的启动流程对于诊断启动问题、优化启动性能以及开发自定义starter都非常重要。希望这篇深入的分析能够帮助你更好地掌握Spring Boot的内部工作机制。