news 2026/4/6 23:10:22

Spring Boot 启动流程源码解析:从 `main()` 到 Web 服务就绪

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring Boot 启动流程源码解析:从 `main()` 到 Web 服务就绪

Spring Boot 启动流程源码解析:从main()到 Web 服务就绪

一句SpringApplication.run()背后,藏着整个 Spring 生态的启动引擎。

你是否曾:

  • 在面试被问:“Spring Boot 启动过程做了哪些事?”
  • 遇到启动慢、Bean 找不到、配置不生效等问题却无从下手?
  • 想自定义启动行为(如动态加载配置、埋点监控),但不知从何切入?

答案,都在SpringApplication.run()的源码里

今天,我们就逐行拆解 Spring Boot 3.x(兼容 2.x)的启动主流程,带你从main()方法一路走到内嵌 Tomcat 启动完成!

一、入口:main()方法

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

看似简单,实则调用了SpringApplication的静态方法:

// SpringApplication.java public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run(new Class<?>[] { primarySource }, args); } public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }

关键点:先构造SpringApplication实例,再调用其run()方法。

二、阶段 1:构造SpringApplication对象

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 1. 推断应用类型(SERVLET / REACTIVE / NONE) this.properties.setWebApplicationType(WebApplicationType.deduceFromClasspath()); // 2. 从 spring.factories 加载 BootstrapRegistryInitializer this.bootstrapRegistryInitializers = new ArrayList<>( getSpringFactoriesInstances(BootstrapRegistryInitializer.class)); // 3. 从 spring.factories 加载 ApplicationContextInitializer setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 4. 从 spring.factories 加载 ApplicationListener setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 5. 推断主配置类(即包含 main 方法的类) this.mainApplicationClass = deduceMainApplicationClass(); }

🔑 核心动作:

  • 推断 Web 类型

    SERVLET:classpath 中存在 Spring MVC 相关类(如 DispatcherServlet) REACTIVE:存在 WebFlux 相关类(如 DispatcherHandler) NONE:非 Web 应用(如批处理、定时任务)
  • 加载扩展点:通过SpringFactoriesLoader读取META-INF/spring.factories中的 SPI 实现。

💡 这就是 Spring Boot自动装配和扩展机制的起点

三、阶段 2:执行run(args)—— 启动主流程

这是最核心的方法,我们分步解析:

步骤 1:准备监听器

SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(bootstrapContext, this.mainApplicationClass);
  • getRunListeners()返回所有SpringApplicationRunListener实例(默认是EventPublishingRunListener
  • starting()发布ApplicationStartingEvent
    → 可用于早期日志初始化、APM 埋点

步骤 2:准备 Environment(环境)

DefaultBootstrapContext bootstrapContext = createBootstrapContext(); ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);

prepareEnvironment()中:

  • 创建Environment(StandardServletEnvironment)
  • 调用environmentPrepared()→ 发布ApplicationEnvironmentPreparedEvent
  • 此时application.properties已加载!

🌟实战价值:Nacos/Apollo 客户端在此阶段注入远程配置!


步骤 3:创建 ApplicationContext(应用上下文)

context = createApplicationContext();

根据webApplicationType选择上下文类型:

  • SERVLETAnnotationConfigServletWebServerApplicationContext
  • REACTIVEAnnotationConfigReactiveWebServerApplicationContext

该上下文继承自GenericApplicationContext,并具备内嵌 Web 容器支持

步骤 4:准备上下文

prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);

内部关键操作:

  • 注册bannerBean
  • 应用所有ApplicationContextInitializer
  • 发布ApplicationContextInitializedEvent

⚠️ 注意:此时Bean 还未实例化,只是定义已加载。

步骤 5:刷新上下文(Refresh)—— 最重量级阶段!

refreshContext(context);

最终调用AbstractApplicationContext.refresh()(Spring Framework 的核心方法):

@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 1. 准备刷新(记录启动时间、设置活跃状态) prepareRefresh(); // 2. 获取 BeanFactory(通常是 DefaultListableBeanFactory) ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 3. 配置 BeanFactory(设置类加载器、表达式解析器等) prepareBeanFactory(beanFactory); // 4. 执行 BeanFactoryPostProcessor(如 @ConfigurationProperties 绑定) invokeBeanFactoryPostProcessors(beanFactory); // 5. 注册 BeanPostProcessor registerBeanPostProcessors(beanFactory); // 6. 初始化 MessageSource(国际化) initMessageSource(); // 7. 初始化事件广播器 initApplicationEventMulticaster(); // 8. 【模板方法】子类可扩展(如 ServletWebServerApplicationContext 会在此启动内嵌容器) onRefresh(); // 9. 注册监听器 registerListeners(); // 10. 实例化所有非懒加载的单例 Bean! finishBeanFactoryInitialization(beanFactory); // 11. 完成刷新(发布 ContextRefreshedEvent) finishRefresh(); } }
🔥 重点子阶段解析:
  • invokeBeanFactoryPostProcessors
    ConfigurationClassPostProcessor扫描@Component@Bean,解析自动配置类(spring.factories中的EnableAutoConfiguration

  • onRefresh()(在 Servlet 上下文中)

    @Override protected void onRefresh() { super.onRefresh(); try { createWebServer(); // 启动内嵌 Tomcat/Jetty } }
  • finishBeanFactoryInitialization
    → 调用preInstantiateSingletons(),触发所有单例 Bean 的创建(包括依赖注入、@PostConstruct

步骤 6:执行 Runner 启动完成

/ 执行 CommandLineRunner / ApplicationRunner callRunners(context, applicationArguments);

✅ 此时服务已完全就绪,可处理请求!

四、启动流程全景图(简化版)

ain() ↓ new SpringApplication() ├── 推断 Web 类型 ├── 加载 Initializers & Listeners ↓ run(args) ├── starting() → ApplicationStartingEvent ├── prepareEnvironment() → 加载 application.properties ├── createApplicationContext() ├── prepareContext() → 注册主配置类 ├── refreshContext() │ ├── invokeBeanFactoryPostProcessors → 自动配置生效 │ ├── onRefresh() → 启动内嵌 Web 容器 │ └── finishBeanFactoryInitialization → 初始化所有 Bean ├── callRunners() → 执行启动后任务

五、学源码有什么用?实战场景举例

场景利用的启动阶段扩展方式
动态加载远程配置environmentPrepared实现EnvironmentPostProcessor
启动耗时分析starting()/running()自定义SpringApplicationRunListener
服务注册延迟ContextRefreshedEvent监听事件,确保 Bean 全部就绪
自定义 BannerprepareContext阶段实现Banner接口
避免循环依赖报错理解finishBeanFactoryInitialization顺序调整依赖关系或使用@Lazy

📌关注我,每天5分钟,带你从 Java 小白变身编程高手!
👉 点赞 + 关注,让更多小伙伴一起进步!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/4 18:23:51

Unity反向遮罩终极指南:打造惊艳UI特效的5个秘诀

Unity反向遮罩终极指南&#xff1a;打造惊艳UI特效的5个秘诀 【免费下载链接】UIMask Reverse Mask of Unity "Mask" component 项目地址: https://gitcode.com/gh_mirrors/ui/UIMask 还在为Unity默认遮罩的局限性而苦恼吗&#xff1f;你可能会遇到这样的情况…

作者头像 李华
网站建设 2026/3/31 20:53:45

3种高效解析百度网盘直链的实用技巧

3种高效解析百度网盘直链的实用技巧 【免费下载链接】baidu-wangpan-parse 获取百度网盘分享文件的下载地址 项目地址: https://gitcode.com/gh_mirrors/ba/baidu-wangpan-parse 还在为百度网盘的下载限速而烦恼吗&#xff1f;当你的网络带宽明明可以全速运行&#xff0…

作者头像 李华
网站建设 2026/4/3 6:19:52

Umi-OCR引擎模块化集成方案与技术实现

Umi-OCR引擎模块化集成方案与技术实现 【免费下载链接】Umi-OCR Umi-OCR: 这是一个免费、开源、可批量处理的离线OCR软件&#xff0c;适用于Windows系统&#xff0c;支持截图OCR、批量OCR、二维码识别等功能。 项目地址: https://gitcode.com/GitHub_Trending/um/Umi-OCR …

作者头像 李华
网站建设 2026/4/4 13:32:43

CTF流量分析神器CTF-NetA:新手也能3分钟上手的实战指南

还在为CTF比赛中的流量分析头疼吗&#xff1f;面对海量的网络数据包&#xff0c;传统工具操作复杂、学习成本高&#xff0c;让许多安全新手望而却步。今天要介绍的CTF-NetA流量分析工具&#xff0c;正是为解决这一痛点而生。 【免费下载链接】CTF-NetA 项目地址: https://gi…

作者头像 李华
网站建设 2026/3/31 2:29:24

游戏库管理革命:插件增强如何让你的游戏体验更智能

游戏库管理革命&#xff1a;插件增强如何让你的游戏体验更智能 【免费下载链接】PlayniteExtensionsCollection Collection of extensions made for Playnite. 项目地址: https://gitcode.com/gh_mirrors/pl/PlayniteExtensionsCollection 想象一下&#xff0c;当你打开…

作者头像 李华
网站建设 2026/4/3 3:27:26

Easy-Scraper智能数据采集:零基础快速上手完整指南

Easy-Scraper智能数据采集&#xff1a;零基础快速上手完整指南 【免费下载链接】easy-scraper Easy scraping library 项目地址: https://gitcode.com/gh_mirrors/ea/easy-scraper 还在为复杂的网页数据提取而烦恼吗&#xff1f;传统爬虫工具需要掌握繁琐的技术细节&…

作者头像 李华