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 文件列表,完成了它光荣的历史使命。

相关推荐
计算机毕设指导65 分钟前
基于微信小程序的电子数据取证知识测试系统【源码文末联系】
java·spring boot·微信小程序·小程序·tomcat·maven·intellij idea
编织幻境的妖7 分钟前
Python with语句与上下文管理器详解
开发语言·数据库·python
JavaBoy_XJ9 分钟前
xxl-job在 Spring Boot 项目中的完整配置指南
java·spring boot·后端·xxl-job配置
水木姚姚11 分钟前
TensorFlow在Microsoft Windows 11下编程
人工智能·windows·python·深度学习·tensorflow·ai编程
B站计算机毕业设计之家12 分钟前
基于python京东商品销售数据分析可视化系统 Django框架 爬虫 大数据(源码)
大数据·爬虫·python·selenium·机器学习·数据分析·django
remaindertime13 分钟前
一文掌握 Spring AI:集成主流大模型的完整方案与思考
后端·spring·ai编程
free-elcmacom13 分钟前
机器学习进阶<1>像侦探一样思考——朴素贝叶斯分类器全解析
大数据·人工智能·python·机器学习·朴素贝叶斯
free-elcmacom13 分钟前
机器学习进阶<4>探索数据中的物以类聚——直观理解k-均值聚类算法
人工智能·python·机器学习·k-means
0思必得016 分钟前
[Web自动化] HTML元素的定位(Xpath)
前端·python·自动化·html·web自动化
ChrisitineTX17 分钟前
Spring Boot 3 + GraalVM Native Image 原理:从启动 10秒 到 0.05秒,AOT 编译到底干了什么?
java·spring boot·后端