Spring Boot 为什么“抛弃”了 spring.factories?

每一位 Spring Boot 开发者都享受过这种"魔法":你只需要在 pom.xml 中添加一个 spring-boot-starter-web 依赖,你的应用就"凭空"获得了启动 Tomcat、配置 Spring MVC 的能力。

你有没有想过,Spring Boot 是如何"发现"并"激活"这些藏在 starter 里的配置的?在 Spring Boot 2.7 之前,这个"魔法"的秘密,就藏在一个看似普通、但却无比核心的文件里------spring.factories

本文将带你深入这个文件,理解它为什么存在(它的天才设计),以及它在 Spring Boot 3.0 之后**为什么又被"废弃"**了。

1. 为什么需要 spring.factories?------ Java SPI 的局限性

spring.factories 出现之前,Java 有一套自己的"服务发现"机制,叫做 SPI (Service Provider Interface)

  • Java SPI 机制: 遵循一个约定:在 JAR 包的 META-INF/services/ 目录下,放置一个以"接口全限定名"命名的文件,文件内容是"实现类的全限定名"。
  • 示例: mysql-connector-java.jar 中有
    • 文件:META-INF/services/java.sql.Driver
    • 内容:com.mysql.cj.jdbc.Driver
      这样,DriverManager 就能通过 ServiceLoader.load(Driver.class) 找到所有实现了 Driver 接口的驱动。

Java SPI 的局限性:

  1. 一个文件一个接口: java.sql.Driver 文件只能存 Driver 的实现,MyService 接口需要另一个 com.example.MyService 文件。
  2. 加载时机: ServiceLoader 通常是按需(懒加载)的。

Spring Boot 的野心:

Spring Boot 在启动时,需要一次性 加载很多种不同的组件,例如:

  • 所有需要自动配置的类 (EnableAutoConfiguration)
  • 所有应用上下文初始化器 (ApplicationContextInitializer)
  • 所有应用启动监听器 (ApplicationListener)
  • 所有启动失败分析器 (FailureAnalyzer)
  • ...

如果用 Java SPI,Spring Boot 启动时就不得不在 Classpath 下扫描几十种不同的 META-INF/services/ 文件,这既混乱又低效。

spring.factories 的诞生:

Spring 团队借鉴了 SPI 的思想,但创造了一个更强大的"变体"。

  • 设计思想: "我们不要几十本零散的电话簿,我们要做一本包罗万象的、分类清晰的'城市黄页'。"
  • 这就是 spring.factories 它始终位于 META-INF/spring.factories一个 固定的位置。文件内部使用 .properties 格式,通过不同的 Key 来分类列出所有需要被加载的组件。

2. spring.factories 的工作原理:一本"黄页"

spring.factories 是一个简单的 .properties 文件,其内容结构如下(以 spring-boot-autoconfigure 包为例):

properties 复制代码
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.LoggingApplicationListener

# Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

# Failure Analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.autoconfigure.jdbc.DataSourceBeanCreationFailureAnalyzer

# Auto Configurations
# 这是最核心的键
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
... (还有上百个)

工作流程图(Spring Boot 2.x):
SpringApplication.run() 启动 调用 SpringFactoriesLoader.loadFactoryNames() 扫描所有 JAR 包的 Classpath 查找所有 'META-INF/spring.factories' 文件 解析所有 .factories 文件
(这是一个 IO 密集型操作) 根据 Key (如 EnableAutoConfiguration)
将所有实现类的全限定名合并到一个 List Spring Boot 容器拿到这个 List 对 List 中的自动配置类
进行 @Conditional 条件判断
只实例化 'matches()' 返回 true 的 Bean 结束: 自动配置完成

图解:

  1. 加载: Spring Boot 启动时,SpringFactoriesLoader 会遍历 Classpath 上的所有 JAR 包,找到所有spring.factories 文件。
  2. 合并: 将这些文件中的内容全部加载到内存,并根据相同的 Key(例如 ...EnableAutoConfiguration)将所有的类名合并到一个 List<String> 中。
  3. 使用: 自动配置模块拿到这个包含上百个配置类名称的 List,然后依次进行 @Conditional 判断,最终只实例化那些满足条件的 Bean。

3. 为什么要"废弃" spring.factories?------ "成也萧何,败也萧何"

spring.factories 这个设计非常成功,以至于它成为了它自己成功的"受害者"。随着 Spring 生态(尤其是 Spring Cloud)的爆炸式增长,这个"黄页"变得不堪重负。

核心痛点:

  1. 性能瓶颈(IO密集):
    spring.factories 是一个字符串列表 。Spring Boot 在每次启动时,都必须:

    • 遍历 Classpath 上的每一个 JAR
    • 打开每一个 spring.factories 文件(可能一个项目中有几十个)。
    • 解析这个(可能很庞大的).properties 文件。
    • 将所有字符串加载到内存中,再去重、过滤。
    • 在大型 Spring Cloud 项目中,这个过程会累积成一个不可忽视的启动时开销
  2. "上帝文件" (God File):

    这个文件什么都管。Listener, Initializer, AutoConfiguration... 所有的扩展点都堆在同一个文件里,职责混乱,可读性差。

  3. AOT/GraalVM 不友好:

    这种基于"扫描 Classpath"和"字符串反射"的机制,对于 AOT (Ahead-of-Time) 编译 和 GraalVM 原生镜像技术非常不友好。AOT 编译器希望在编译时就明确知道哪些 Bean 需要被创建,而不是在运行时才去动态扫描和发现。

4. "新王当立":Spring Boot 3.0+ 的新约定

从 Spring Boot 2.7 开始(并在 3.0 中成为正式标准),Spring 团队引入了一种新的、更高效、更专一的自动配置加载机制,彻底取代了 spring.factories

新约定:

不再使用一个统一的 spring.factories 文件,而是为每一种 SPI 扩展点,提供一个专属的独立的文件

文件路径: META-INF/spring/ (注意,不再是 services/)

扩展点类型 Spring Boot 2.x (旧) Spring Boot 3.x (新)
自动配置 META-INF/spring.factories ...EnableAutoConfiguration META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
启动监听器 META-INF/spring.factories ...ApplicationListener META-INF/spring/org.springframework.context.ApplicationListener.imports
启动初始化器 META-INF/spring.factories ...ApplicationContextInitializer META-INF/spring/org.springframework.context.ApplicationContextInitializer.imports
... ... ...

新机制的优势:

  1. 职责单一: AutoConfiguration.imports 文件中只包含 自动配置类。ApplicationListener.imports 文件中只包含监听器。结构极其清晰。
  2. 性能提升 (按需加载): Spring Boot 启动时,如果它只需要 加载自动配置,它会直奔 AutoConfiguration.imports 这一个文件去读取,而完全无视 其他如 ApplicationListener.imports 等文件。
  3. AOT 友好: 这种确定性的、专一的文件格式,使得 AOT 编译器在构建时可以非常容易地静态分析和处理。

示例:spring.factories vs. AutoConfiguration.imports

  • 旧(spring.factories):

    properties 复制代码
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    com.example.MyAutoConfig1,\
    com.example.MyAutoConfig2
  • 新(AutoConfiguration.imports):

    复制代码
    com.example.MyAutoConfig1
    com.example.MyAutoConfig2

    (文件内容就是类名的简单列表,解析更快)

5. 总结

spring.factories 是 Spring Boot 早期实现"约定优于配置"和自动装配的天才设计。它通过一份"黄页",巧妙地解决了 Java SPI 的局限性,支撑起了 Spring Boot 庞大的生态系统。

然而,随着项目规模的增长和云原生(AOT)时代的到来,这个"什么都管"的"上帝文件"逐渐成为了性能瓶颈。Spring Boot 3.0 将其"废弃",进化为更专一、更轻量、更高性能META-INF/spring/*.imports 文件列表,完成了它光荣的历史使命。

相关推荐
天若有情6734 分钟前
Spring Boot 前后端联调3大经典案例:从入门到实战(通俗易懂版)
spring boot·后端·状态模式
sin22015 分钟前
Spring事务管理(SpringBoot)
java·spring boot·spring
C***11506 分钟前
Spring TransactionTemplate 深入解析与高级用法
java·数据库·spring
BD_Marathon7 分钟前
SpringBoot——配置文件格式
java·spring boot·后端
indexsunny10 分钟前
互联网大厂Java面试实战:Spring Boot与微服务在电商场景的应用解析
java·spring boot·redis·微服务·kafka·gradle·maven
万行10 分钟前
机器学习&第一章
人工智能·python·机器学习·flask·计算机组成原理
2301_7973122612 分钟前
学习java37天
开发语言·python
幽络源小助理14 分钟前
SpringBoot+小程序高校素拓分管理系统源码 – 幽络源免费分享
spring boot·后端·小程序
计算机学姐20 分钟前
基于SpringBoot的汉服租赁系统【颜色尺码套装+个性化推荐算法+数据可视化统计】
java·vue.js·spring boot·后端·mysql·信息可视化·推荐算法
WJSKad123521 分钟前
果园树干识别与定位_faster-rcnn_x101-32x4d_fpn_1x_coco改进实践
python