当前位置: 首页 > news >正文

Spring Boot 源码研读之 SpringApplication 对象的创建

SpringApplication 对象创建

一般 Spring Boot 项目都是通过在main函数中执行SpringApplication.run(XXXX.class,args);来启动的,而run方法的内部执行首先会进行SpringApplication对象的创建,让我们来看看SpringApplication对象创建做了哪些事。

通过代码跟踪最终 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)); this.webApplicationType = WebApplicationType.deduceFromClasspath(); this.bootstrapRegistryInitializers = new ArrayList<>( getSpringFactoriesInstances(BootstrapRegistryInitializer.class)); setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }

通过代码分析,我可以看到 SpringApplication 对象的创建主要有如下操作:

  • resourceLoader:默认赋值null
  • primarySources:Set<Class<?>> 存放当前启动类 XXXX.class 集合,注意:启动类可能在其他位置
  • webApplicationType:通过判断相关类是否加载,确定web服务类型是 REACTIVE,还是 SERVLET。
  • bootstrapRegistryInitializers通过加载并读取META-INF/spring.factories文件中的配置并结合反射的方式来实例化所有BootstrapRegistryInitializer接口的实现类。
  • setInitializers:通过加载并读取META-INF/spring.factories文件中的配置并结合反射的方式来实例化所有ApplicationContextInitializer接口的实现类。
  • setListeners:通过加载并读取META-INF/spring.factories文件中的配置并结合反射的方式来实例化所有ApplicationListener接口的实现类。
  • mainApplicationClass:通过当前线程的堆栈来找到main()函数所在的类,即启动类

getSpringFactoriesInstances(Clazz.class)实现

getSpringFactoriesInstances方法的实现如下图所示,核心逻辑就是读取META-INF/spring.factories配置文件,并通过反射的方式创建指定接口类型的实现类实例。具体使用也可以参考另外一篇文章 《Spring 源码之 SpringFactoriesLoader 类简介-CSDN博客》

通过这一步我们就获取到了所有BootstrapRegistryInitializerApplicationContextInitializerApplicationListener接口的实现类。

补充

spring.factories加载机制

  • 核心机制SpringFactoriesLoader.loadSpringFactories()classpath*:/META-INF/spring.factories,解析key=全限定接口名,value=实现类全限定名,用反射newInstance()批量实例化。
  • 那为什么SpringApplication 在容器还没创建时就能拿到这些扩展点?
    • 因为它走的是classpath 静态扫描 + 反射不依赖 BeanFactory。这是 Spring Boot启动期扩展点机制,和运行期的BeanPostProcessor是两套体系。
  • 演进史
    • Spring Boot 2.7+:引入META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports(一行一个自动配置类),逐步替代 spring.factories 里的 auto-configuration 部分
    • Spring Boot 3.0+:spring.factories 里的 auto-configuration 段正式废弃,仅保留少量非 auto-configuration 段
    • Spring Boot 3.2+:spring.factories 文件完全废弃(实际并没有完全废弃
  • 原因:spring.factories 是一个 key-value 巨型文件,所有扩展点都堆在一起,加载时全量解析;改成*.imports按需加载,启动更快、更易维护
  • 总结一句话:"SpringApplication 构造函数用 classpath 扫描 + 反射加载扩展点,绕开 Spring 容器,让扩展点在容器起来之前就能用。2.7 之后 Spring 把这条路从 spring.factories 拆成 .imports,3.2 完全废弃,本质是从'全量加载'变'按需加载'。"

WebApplicationType补充

  • 三种类型REACTIVE(WebFlux)/SERVLET(Spring MVC)/NONE(非 Web 应用)
  • 判断逻辑deduceFromClasspath()ClassUtils.isPresent依次探测:
    • 存在DispatcherServletSERVLET(但这个判断有问题,见下)
    • 存在WebFlux相关类且没有DispatcherServletREACTIVE
    • 都不存在 →NONE
  • ⚠️ 误判场景:项目引了spring-web(因为某个工具包传递依赖),没真用 Spring MVC,可能被误判为 SERVLET,启动报"找不到 DispatcherServlet"或加载多余 MVC Bean
  • 解法:手动setWebApplicationType(WebApplicationType.NONE),或在 SpringApplication 启动前排除多余依赖

deduceMainApplicationClass 链式启动支持

  • 实现new RuntimeException().getStackTrace()拿堆栈,遍历找到main方法所在类
  • 为什么用堆栈而不是参数传入?为了支持SpringApplicationBuilder链式/嵌套启动——用户可能从SpringBootServletInitializer或别的启动类拉起,主类在运行期才确定
  • Trade-off实现简单(不传参) vs 启动类可动态决定——选后者

注:spring boot 版本为3.2.3

http://www.jsqmd.com/news/1105781/

相关文章:

  • 大规模服务集成中的限流设计:保护上游也保护业务
  • 宇宙常数即超复数空间广义分形维数统一猜想及实例论证
  • 检测 win10 硬件部分的 powershell
  • 计算机Java毕设实战-基于 Java 的学术文献资源分类检索系统的设计与实现 基于 Java 的数字化文献资料归档管理系统【完整源码+LW+部署说明+演示视频,全bao一条龙等】
  • AI 搜索新时代,好客搜智搜 GEO 系统搭建企业长效 AI 全域运营渠道
  • Pixel2Geo单目视觉解算协同增量网格渲染:像素驱动高精度空间重建优化算法
  • Kafka 高可用架构:副本数不是越多越安全
  • 原生一体化渲染管线破算力卡顿桎梏,全域像素同源融合消实景画面割裂难题
  • DeFi 协议收益率数学模型与风险量化分析
  • 微软Memora如何破解智能体的长期记忆难题
  • 像素几何映射与分布式3D图形渲染耦合架构:广域视频孪生动态世界模型构建研究
  • 一站式企业数字化运营平台,解读好客搜全产品线协同技术优势
  • 2026年度智能编码工具深度横评:引入Coding Agent的团队,人均代码吞吐量提升35%以上
  • 为什么途鸽求职的求职辅导效果这么好?
  • 小众且实用,这软件是真神器!
  • MH迈汇:从公开信息出发,拆解风控思路与流程清晰度
  • 初等数学研究教材PDF电子版分享
  • 企业级检索增强 后端集成:Java 服务如何管理知识库版本
  • 抖音无水印下载终极指南:5分钟学会批量下载高清视频的完整教程
  • Python数据库编程实战:从psycopg3到SQLAlchemy Core — PostgreSQL篇
  • PMSM在线转动惯量辩识+滑模负载转矩观测器(仿真+参考文献+理论公式文档)
  • 别再“面向百度编程”了!AI 时代,我们要做“指挥家”而不是“搬砖工”
  • 开发者必读:openeuler/cdf-crypto API接口全解析(附代码示例)
  • HarmonyOS NEXT ArkTS 背景图片模式实战——Fill / Contain / Cover 三种缩放模式深度解析
  • 如何在Windows 11 LTSC系统一键安装微软商店:完整指南
  • Windows 11g在线库迁移及搭建双机
  • STM32寄存器开发练习(二):GPIO的工作模式
  • 基于BP神经网络的交通标志识别系统设计与实现
  • MH迈汇:从执行效率切入的标准评估
  • LLM上下文工程:从Prompt设计到记忆系统的架构演进