目录

前言

启动流程概览

一、第一阶段:初始化SpringApplication

二、第二阶段:运行SpringApplication

三、第三阶段:环境准备

四、第四阶段:创建应用上下文

五、第五阶段:准备应用上下文

六、第六阶段:刷新应用上下文(核心)

七、第七阶段:启动后处理

启动流程图解

Spring Boot启动流程与自动装配的联系

总结


前言

        Spring Boot以其"开箱即用"的特性大大简化了Spring应用的开发部署流程。只需一个main方法和一个简单的SpringApplication.run()调用,我们的应用就能快速启动。但这背后究竟发生了什么?本文将深入剖析Spring Boot应用的完整启动流程,带你理解从点击"运行"到应用完全就绪的每一个关键步骤。


启动流程概览

Spring Boot的启动过程可以概括为以下几个核心阶段:

  1. 初始化SpringApplication实例

  2. 运行SpringApplication

  3. 准备环境设置

  4. 创建应用上下文

  5. 刷新应用上下文(核心)

  6. 执行Runner接口实现

下面我们详细分析每个阶段的具体工作。

一、第一阶段:初始化SpringApplication

        当我们调用SpringApplication.run(Application.class, args)时,首先会初始化一个SpringApplication实例

//代码示例
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的构造函数中,会进行以下关键操作:

  1. 推断应用类型:根据类路径下的依赖判断是SERVLET应用(Spring MVC)、REACTIVE应用(WebFlux)还是普通应用

  2. 加载应用上下文初始化器:通过SpringFactoriesLoader从META-INF/spring.factories加载ApplicationContextInitializer

  3. 加载应用监听器:同样通过SpringFactoriesLoader加载ApplicationListener

  4. 推断主配置类:根据堆栈信息找到包含main方法的类

二、第二阶段:运行SpringApplication

run方法是整个启动流程的核心:

public ConfigurableApplicationContext run(String... args) {// 1. 创建启动计时器StopWatch stopWatch = new StopWatch();stopWatch.start();// 2. 初始化默认应用上下文ConfigurableApplicationContext context = null;// 3. 配置headless属性configureHeadlessProperty();// 4. 获取SpringApplicationRunListenersSpringApplicationRunListeners listeners = getRunListeners(args);// 5. 发布应用开始启动事件listeners.starting();try {// 6. 准备环境ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);// 7. 打印BannerBanner printedBanner = printBanner(environment);// 8. 创建应用上下文context = createApplicationContext();// 9. 准备应用上下文prepareContext(context, environment, listeners, applicationArguments, printedBanner);// 10. 刷新应用上下文(核心步骤)refreshContext(context);// 11. 上下文刷新后处理afterRefresh(context, applicationArguments);// 12. 停止计时器stopWatch.stop();// 13. 发布应用启动完成事件if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}listeners.started(context);// 14. 调用ApplicationRunner和CommandLineRunnercallRunners(context, applicationArguments);} catch (Throwable ex) {handleRunFailure(context, ex, listeners);throw new IllegalStateException(ex);}try {listeners.running(context);} catch (Throwable ex) {handleRunFailure(context, ex, null);throw new IllegalStateException(ex);}return context;
}

三、第三阶段:环境准备

prepareEnvironment()方法负责准备应用运行环境:

  1. 创建环境对象:根据应用类型创建StandardEnvironment或StandardServletEnvironment

  2. 配置环境:配置PropertySources和Profiles

  3. 发布环境准备事件:通过EnvironmentPostProcessorApplicationListener处理

  4. 绑定环境到SpringApplication

  5. 转换配置:将命令行参数转换为PropertySource

  6. 处理ConfigurationProperties:验证和绑定@ConfigurationProperties

四、第四阶段:创建应用上下文

createApplicationContext()根据应用类型创建对应的应用上下文:

  • Servlet应用:AnnotationConfigServletWebServerApplicationContext

  • Reactive应用:AnnotationConfigReactiveWebServerApplicationContext

  • 普通应用:AnnotationConfigApplicationContext

五、第五阶段:准备应用上下文

prepareContext()方法准备创建好的应用上下文:

  1. 设置环境

  2. 后处理上下文:调用ApplicationContextInitializer

  3. 发布上下文准备事件

  4. 注册SpringBootBanner

  5. 设置资源加载器和类加载器

  6. 注册Bean定义

    • 注册主配置类(@SpringBootApplication标注的类)

    • 注册命令行参数Bean

    • 注册Banner Bean

六、第六阶段:刷新应用上下文(核心)

refreshContext()调用的是AbstractApplicationContext的refresh()方法,这是Spring容器的核心生命周期方法:

public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// 1. 准备刷新上下文prepareRefresh();// 2. 获取刷新后的BeanFactoryConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// 3. 准备BeanFactory使用上下文prepareBeanFactory(beanFactory);try {// 4. 允许BeanFactory的后处理postProcessBeanFactory(beanFactory);// 5. 调用BeanFactoryPostProcessorinvokeBeanFactoryPostProcessors(beanFactory);// 6. 注册BeanPostProcessorregisterBeanPostProcessors(beanFactory);// 7. 初始化MessageSourceinitMessageSource();// 8. 初始化事件广播器initApplicationEventMulticaster();// 9. 初始化特殊Bean(由子类实现)onRefresh();// 10. 注册监听器registerListeners();// 11. 完成BeanFactory初始化,实例化所有非懒加载单例finishBeanFactoryInitialization(beanFactory);// 12. 完成刷新,发布上下文刷新事件finishRefresh();} catch (BeansException ex) {// 13. 销毁已创建的单例BeandestroyBeans();// 14. 重置激活标志cancelRefresh(ex);throw ex;} finally {// 15. 重置Spring核心中的公共内省缓存resetCommonCaches();}}
}

对于Spring Boot来说,onRefresh()方法尤为重要,这里会创建嵌入式Web服务器:

protected void onRefresh() {super.onRefresh();try {createWebServer(); // 创建Tomcat、Jetty或Undertow服务器} catch (Throwable ex) {throw new ApplicationContextException("Unable to start web server", ex);}
}

七、第七阶段:启动后处理

刷新完成后,Spring Boot会执行一些后处理操作:

  1. 调用ApplicationRunner和CommandLineRunner:执行应用中定义的Runner实现

  2. 发布应用就绪事件:ApplicationReadyEvent,表示应用已完全启动

  3. 启动完成:此时应用已完全就绪,可以处理请求

启动流程图解

Spring Boot启动流程与自动装配的联系

        1.核心关系:自动装配是启动流程的关键环节

        简单来说,自动装配是Spring Boot启动流程中的一个核心子过程。没有启动流程提供的环境、上下文和机制,自动装配无法工作;而没有自动装配,Spring Boot的启动就失去了"智能"和"自动化"的特性,退回到了传统Spring应用的繁琐配置模式。

        2.自动装配的具体执行时机

        自动装配发生在应用上下文刷新阶段,具体在refreshContext()方法中的invokeBeanFactoryPostProcessors(beanFactory)步骤:

public void refresh() throws BeansException, IllegalStateException {// ... 前面的步骤invokeBeanFactoryPostProcessors(beanFactory); // 自动装配在这里发生!// ... 后续步骤
}


总结

        Spring Boot的启动流程是一个精心设计的过程,它将传统的XML配置转换为基于Java的自动配置,通过条件化装配和自动发现机制,极大地简化了Spring应用的开发和部署。理解这个流程不仅有助于我们更好地使用Spring Boot,还能在遇到问题时快速定位和解决。

        整个过程体现了Spring Boot的核心设计理念:约定优于配置自动装配微内核架构。通过事件监听机制和扩展点设计,Spring Boot在保持简洁性的同时,也提供了极大的灵活性和可扩展性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/94349.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/94349.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/94349.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Matplotlib 可视化大师系列(三):plt.bar() 与 plt.barh() - 清晰对比的柱状图

目录Matplotlib 可视化大师系列博客总览Matplotlib 可视化大师系列&#xff08;三&#xff09;&#xff1a;plt.bar() 与 plt.barh() - 清晰对比的柱状图一、 柱状图是什么&#xff1f;何时使用&#xff1f;二、 函数原型与核心参数plt.bar(x, height, ...) - 垂直柱状图plt.ba…

基于 FastAPI 和 OpenFeature 使用 Feature Flag 控制业务功能

模拟业务场景&#xff1a;多租户系统跨域转账&#xff0c;需要控制某租户下某用户是否可以在某域转账 open_feature_util.py import typing from abc import abstractmethod, ABCMeta from typing import Sequencefrom openfeature.evaluation_context import EvaluationContex…

Stm32通过ESP8266 WiFi连接阿里云平台

本文将介绍stm32如何通过WiFi来连接阿里云&#xff0c;上传数据和接收指令。要先与阿里云建立TCP连接&#xff0c;然后再通过MQTT协议交互。 大体流程&#xff1a;1、在阿里云网页上创建产品和设备&#xff1b;2、stm32通过WiFi连接云平台&#xff1b;3、MQTT连接阿里云&#…

北京-测试-入职甲方金融-上班第三天

今日上班时间9-20.18&#xff0c;再加42分钟就可以拿到75块钱了&#xff0c;但我想回家&#xff0c;所以下班今天上午有人事举办的入职培训&#xff0c;下午有业务培训&#xff0c;培训完领导给我安排了两个需求。慌死&#xff0c;吓死&#xff0c;我都不懂&#xff0c;业务和工…

Java基础第2天总结

使用switch时注意事项&#xff1a;表达式类型只能是byte、short、int、char,JDK5开始支持枚举&#xff0c;JDK7开始支持String&#xff0c;不支持double、float、long(精确度问题&#xff0c;小数有点不精确)。case给出的值不允许重复&#xff0c;且只能是字面量&#xff0c;不…

鸿蒙开发中的List组件详解

目录 引言 1.List组件基础 2.List接口参数 1.space 2.initialIndex 3.scroller 3.ListView的属性 1.listDirection 2.lanes 3.divider 4.scrollBar 4.布局与约束 5.ListItem生命周期 1.使用ForEach创建ListItem 2.使用LazyForEach创建ListItem 3…

2026界计算机专业毕业的有福了!(开题报告任务书)

开题报告 我们以基于Java的婚纱店管理系统为案例进行指导。 任务书&#xff1a; 首先是毕设的立题依据&#xff0c;这个主要描写一些简洁大体的大白话&#xff0c;描述一下你为什么要做这个题目的毕设。 那就需要你描述一下现阶段社会面婚纱店的运营情况&#xff0c;写一些…

安全、高效、可靠的物理隔离网络安全专用设备———信刻光盘安全隔离与文件单向导入系统!

着各种数据传输、储存技术、信息技术的快速发展&#xff0c;保护信息安全是重中之重。军工企业、政府、部队及企事业单位等利用A网与B网开展导入/导出相关工作已成为不可逆转的趋势。针对于业务需要与保密规范相关要求&#xff0c;涉及重要秘密信息&#xff0c;需做到完全的物理…

JetPack 与 PyTorch 版本对应及资源详情

下载链接 JetPack 版本适配 PyTorch 版本发布日期可下载资源&#xff08;.whl 安装包 / 文档&#xff09;JP 6.1PyTorch 2.5.0a0&#xff08;构建号&#xff1a;872d972e41.nv24.08.17622132&#xff09;2024/10/01- torch-2.5.0a0872d972e41.nv24.08.17622132-cp310-cp310-li…

【c++进阶系列】:万字详解多态

&#x1f525; 本文专栏&#xff1a;c &#x1f338;作者主页&#xff1a;努力努力再努力wz &#x1f4aa; 今日博客励志语录&#xff1a; 你以为自己在孤独地爬坡吗&#xff1f;看看身后吧——那些被汗水浸湿的脚印&#xff0c;早已连成一道向上的阶梯 ★★★ 本文前置知识&am…

AI+预测3D新模型百十个定位预测+胆码预测+去和尾2025年8月23日第168弹

从今天开始&#xff0c;咱们还是暂时基于旧的模型进行预测&#xff0c;好了&#xff0c;废话不多说&#xff0c;按照老办法&#xff0c;重点8-9码定位&#xff0c;配合三胆下1或下2&#xff0c;杀1-2个和尾&#xff0c;再杀4-5个和值&#xff0c;可以做到100-300注左右。(1)定位…

分布式搜索(Elasticsearch)深入用法

目录 数据聚合 聚合的种类 DSL实现聚合 桶聚合 度量聚合 RestAPI实现聚合 多条件聚合 自动补全 拼音分词器 自定义分词器 自动补全查询 实现搜索框自动补全 数据同步 数据同步思路分析 实现elasticsearch与数据库数据同步 集群 搭建ES集群 集群脑裂问题 集群…

java:接口与实现类

1. Java 基础层面接口&#xff08;Interface&#xff09; 只是方法的定义/规范&#xff0c;里面没有真正的逻辑。例如&#xff1a;public interface IBookService {boolean save(Book book); }&#x1f449; 这里只说明了&#xff1a;我要有一个 save 方法&#xff0c;但“怎么…

Chrome 插件开发实战:从入门到进阶

1.1 Chrome 插件的魅力与应用场景Chrome 插件是增强 Chrome 浏览器功能的得力助手&#xff0c;能实现广告拦截、密码管理、标签管理等实用功能。在日常办公中&#xff0c;我们可以借助插件提升效率&#xff0c;如自动填充表单、快速保存网页内容&#xff1b;在浏览网页时&#…

通过官方文档详解Ultralytics YOLO 开源工程-熟练使用 YOLO11实现分割、分类、旋转框检测和姿势估计(附测试代码)

目录 前言&#xff1a; 1.了解ultralytics工程与yolo模型 1.1 yolo11可以为我们做些什 1.2 yolo11模型的高性能 1.3 对于yolo11一些常见的问题 1.3.1 YOLO11 如何以更少的参数实现更高的精度&#xff1f; 1.3.2 YOLO11 可以部署在边缘设备上吗&#xff1f; 2. 深入了解y…

vue实现小程序oss分片上传

随着小程序越来越普及,小程序上传文件必不可少,那么上传的文件大小就不可控了,小则几mb,大到好几百mb,小文件还可以,但是一到超过200mb或稍微再大些的小程序就很容易上传失败,导致功能不能继续进行。以下我们就来解决这个问题,将大文件实现分片上传 温馨提示,不要看内…

14.Shell脚本修炼手册--玩转循环结构(While 与 Until 的应用技巧与案例)

while 循环和 until 循环的应用实践 文章目录while 循环和 until 循环的应用实践当型和直到型循环&#xff1a;两种 "重复" 的逻辑while 循环&#xff1a;满足条件就继续until 循环&#xff1a;不满足条件就继续基础示例&#xff1a;从简单场景学用法示例 1&#xff…

chromadb使用hugging face模型时利用镜像网站下载注意事项

chromadb默认使用sentence-transformers/all-MiniLM-L6-v2的词嵌入&#xff08;词向量&#xff09;模型&#xff0c;如果在程序首次运行时&#xff0c;collection的add或query操作时如果没有指定embeddings或query_embeddings&#xff0c;程序会自动下载相关嵌入向量模型&#…

基于大模型的对话式推荐系统技术架构设计

注&#xff1a;此文章内容均节选自充电了么创始人&#xff0c;CEO兼CTO陈敬雷老师的新书《GPT多模态大模型与AI Agent智能体》&#xff08;跟我一起学人工智能&#xff09;【陈敬雷编著】【清华大学出版社】 清华《GPT多模态大模型与AI Agent智能体》书籍配套视频课程【陈敬雷…

第1章 React组件开发基础

在掌握React开发之前,我们需要先建立扎实的组件开发基础。这些基础知识不仅影响你的开发效率,更决定了应用程序的性能、可维护性和团队协作的顺畅程度。 本章将深入探讨React组件开发的核心技巧,从JSX语法优化到组件架构设计,帮你建立正确的React开发思维模式。 🗂️ 本…