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

相关推荐
小七mod1 小时前
【Spring】Spring Boot自动配置的案例
java·spring boot·spring·自动配置·源码·ioc·aop
清晨细雨~2 小时前
SpringBoot整合EasyExcel实现Excel表头校验
spring boot·后端·excel
sg_knight2 小时前
RabbitMQ 中的预取值(prefetch)详解:如何真正提升消费端性能?
java·spring boot·spring·spring cloud·消息队列·rabbitmq·预取值
2501_941111822 小时前
使用Python进行网络设备自动配置
jvm·数据库·python
源码之家2 小时前
基于python租房大数据分析系统 房屋数据分析推荐 scrapy爬虫+可视化大屏 贝壳租房网 计算机毕业设计 推荐系统(源码+文档)✅
大数据·爬虫·python·scrapy·数据分析·推荐算法·租房
源码之家2 小时前
机器学习:基于python租房推荐系统 预测算法 协同过滤推荐算法 房源信息 可视化 机器学习-线性回归预测模型 Flask框架(源码+文档)✅
大数据·python·算法·机器学习·数据分析·线性回归·推荐算法
循环过三天2 小时前
7.7、Python-常用内置函数
笔记·python·学习
闲人编程2 小时前
【指南】为你的开源Python项目编写完善的文档(Sphinx)
python·开源·文档·sphinx·算法改进·codecapsule
u***1373 小时前
【SpringBoot】【log】 自定义logback日志配置
java·spring boot·logback