首页 > 项目 > 当前页面

Spring Boot 启动流程详解(源码级分析)

2026-06-26 NEW个对象

🚀 Spring Boot 启动流程详解(源码级分析)

Spring Boot最大的优势就是"开箱即用",但它背后实际上完成了配置加载、环境初始化、容器创建、Bean实例化、自动装配、Tomcat启动等大量工作。理解Spring Boot启动流程,不仅有助于阅读源码,也是Java高级开发和面试中的高频考点。

📌 一、问题背景

在传统Spring项目中,需要大量XML配置、Servlet注册以及各种初始化工作,而Spring Boot通过一个简单的main方法即可完成整个应用启动。

@SpringBootApplication public class DemoApplication { ``` public static void main(String[] args) { SpringApplication.run(DemoApplication.class,args); } ``` }

看似只有一行代码,实际上Spring Boot内部完成了数百个类之间的协同工作,最终构建出完整的IOC容器并启动Web服务器。

💡 核心问题:

SpringApplication.run()内部到底做了哪些事情?Bean是什么时候创建的?自动装配什么时候发生?Tomcat又是什么时候启动的?

🎯 二、核心原理

Spring Boot启动的本质,就是创建Spring IOC容器,并在容器初始化过程中完成Bean注册、自动配置、依赖注入以及Web服务器启动。

整个启动过程可以分为九个核心阶段:

  • 创建SpringApplication对象
  • 推断应用类型
  • 加载ApplicationContext
  • 准备Environment环境
  • 创建IOC容器
  • 执行BeanFactoryPostProcessor
  • 实例化所有Bean
  • 启动内嵌Tomcat
  • 执行Runner回调,启动完成
✅ 一句话总结:

Spring Boot启动流程本质就是:创建SpringApplication → 创建IOC容器 → 加载Bean → 自动装配 → 启动Tomcat → 对外提供服务。

📊 三、数据结构分析

SpringApplication ``` │ ▼ ``` ApplicationContext ``` │ ▼ ``` BeanFactory(DefaultListableBeanFactory) ``` │ ▼ ``` BeanDefinition ``` │ ▼ ``` Bean ``` │ ▼ ``` Embedded Tomcat

SpringApplication负责整个启动流程,而真正存放Bean的是BeanFactory。BeanDefinition保存Bean的元数据,真正实例化完成后才会变成Bean对象。

🔥 四、算法分析

SpringApplication.run()可以抽象为下面几个步骤:

run() ↓ 创建SpringApplication ↓ 准备Environment ↓ 创建ApplicationContext ↓ 准备BeanFactory ↓ 加载BeanDefinition ↓ 刷新容器(refresh) ↓ 实例化Bean ↓ 启动Tomcat ↓ 执行Runner ↓ 启动完成

真正最重要的方法其实只有一个,那就是refresh()。

💡 面试中经常说:

Spring Boot启动流程,其实80%的工作都是在AbstractApplicationContext.refresh()里面完成的。

🚀 五、执行流程

main() ``` │ ▼ ``` SpringApplication.run() ``` │ ▼ ``` 创建SpringApplication ``` │ ▼ ``` 准备Environment ``` │ ▼ ``` 创建ApplicationContext ``` │ ▼ ``` prepareContext() ``` │ ▼ ``` refresh() ``` │ ├──────── prepareRefresh() ├──────── obtainFreshBeanFactory() ├──────── invokeBeanFactoryPostProcessors() ├──────── registerBeanPostProcessors() ├──────── initMessageSource() ├──────── initApplicationEventMulticaster() ├──────── onRefresh() │ │ │ ▼ │ 启动Tomcat │ ├──────── finishBeanFactoryInitialization() │ │ │ ▼ │ 创建所有单例Bean │ └──────── finishRefresh() │ ▼ 执行ApplicationRunner │ ▼ Spring Boot启动完成 ```

💡 六、实际案例

案例一:SpringApplication.run()

SpringApplication.run(Application.class,args);

进入run方法后,首先创建SpringApplication对象,并推断当前应用属于Servlet、Reactive还是普通Java程序。

案例二:refresh()刷新容器

refresh(){ ``` prepareRefresh(); obtainFreshBeanFactory(); invokeBeanFactoryPostProcessors(); registerBeanPostProcessors(); finishBeanFactoryInitialization(); finishRefresh(); ``` }

refresh()是整个Spring生命周期的核心,它负责BeanDefinition加载、Bean后处理器注册、Bean实例化以及容器最终初始化。

案例三:Bean创建流程

BeanDefinition ``` │ ▼ ``` 实例化对象 ``` │ ▼ ``` 属性注入 ``` │ ▼ ``` Aware接口 ``` │ ▼ ``` BeanPostProcessor Before ``` │ ▼ ``` @PostConstruct ``` │ ▼ ``` InitializingBean ``` │ ▼ ``` init-method ``` │ ▼ ``` BeanPostProcessor After ``` │ ▼ ``` Bean放入IOC容器
⚠️ 注意:

Tomcat启动并不是SpringApplication.run()开始时就启动,而是在refresh()阶段的onRefresh()过程中启动内嵌Web服务器,之后才开始监听HTTP请求。

✅ 七、优缺点分析

优点 说明
自动配置 减少大量XML配置,提高开发效率
内嵌Web服务器 无需部署WAR即可运行
IOC生命周期完善 统一管理Bean生命周期和依赖关系
扩展能力强 支持Starter、自动装配、监听器、事件机制等

🎯 八、面试常见问题

Q1:Spring Boot启动入口是什么?

main方法中的SpringApplication.run()就是整个Spring Boot应用的启动入口。

Q2:Spring Boot启动过程中最核心的方法是什么?

AbstractApplicationContext.refresh()。IOC容器创建、Bean实例化、自动装配以及Tomcat启动几乎都围绕refresh()展开。

Q3:Bean什么时候创建?

BeanDefinition加载完成后,在refresh()的finishBeanFactoryInitialization()阶段实例化所有非懒加载单例Bean。

Q4:Tomcat什么时候启动?

Tomcat并不是main方法启动时立即启动,而是在refresh()过程中调用onRefresh()创建并启动内嵌Tomcat,随后开始监听HTTP端口。

Q5:Spring Boot自动装配发生在哪一步?

自动装配依赖@EnableAutoConfiguration加载AutoConfiguration配置类,在refresh()过程中注册BeanDefinition,并最终完成Bean实例化和依赖注入。

📌 九、总结

Spring Boot启动流程可以浓缩为下面九个步骤:

main() ↓ SpringApplication.run() ↓ 创建SpringApplication ↓ 准备Environment ↓ 创建ApplicationContext ↓ refresh() ↓ 创建Bean ↓ 启动Tomcat ↓ 执行Runner ↓ 应用启动完成

对于高级开发者而言,需要重点掌握SpringApplication.run()、ApplicationContext创建、refresh()生命周期、Bean实例化、自动装配原理以及Tomcat启动时机。其中,refresh()方法是整个Spring Boot启动流程的核心,也是阅读Spring源码和面试时最值得深入理解的部分。

相关文章

  • 程序员面试技术面和HR面试,最后要问什么问题?

    程序员面试技术面和HR面试,最后要问什么问题?

    NEW个对象 2026-06-14

  • 如果打印日志遇到瓶颈该怎么办?

    如果打印日志遇到瓶颈该怎么办? 1、减少日志的输出 2、降低日志输出级别 3、日志做模块拆分,不同的模块打印到不同的文件里 4、异步日志 5、配置文件大小,当超过文件大小的时候,会创建一个新文件。

    NEW个对象 2024-11-03

  • 秒杀系统线程池削峰,包括代码实现

    线程池削峰的本质是通过控制系统同时处理请求的数量,将瞬时洪峰流量转化为平稳流量。对于秒杀场景而言,线程池相当于一个“缓冲池”,大量请求先进入队列排队,再由固定数量的工作线程逐步处理,从而保护数据库、Redis、MQ等后端资源不被瞬时流量冲垮。

    NEW个对象 2026-06-13

NEW个对象 NEW个对象
JAVA是世界上最好的语言