SpringBoot 3 替换 spring.factories

news/2025/3/26 11:17:40/文章来源:https://www.cnblogs.com/jingzh/p/18793407

目录
  • 1 SpringBoot 3 与 spring.factories
    • 1.1 引言
    • 1.2 spring.factories是什么
      • 1.2.1 基本概念
      • 1.2.2 主要用途
      • 1.2.3 工作原理
    • 1.3 为什么要取消spring.factories
      • 1.3.1 性能问题
      • 1.3.2 缺乏模块化支持
      • 1.3.3 缺乏条件加载能力
      • 1.3.4 配置分散难以管理
      • 1.3.5 GraalVM原生镜像支持
    • 1.4 替代方案 imports 文件
      • 1.4.1 新机制介绍
      • 1.4.2 新机制优势
      • 1.4.3 示例对比
      • 1.4.4 @AutoConfiguration
    • 1.5 迁移指南
      • 1.5.1 自动配置类迁移
      • 1.5.2 其他扩展点如何迁移
      • 1.5.3 自定义扩展点迁移
    • 1.6 SpringFactoriesLoader 变化
      • 1.6.1 API变更
      • 1.6.2 兼容性考虑
    • 1.7. 实战示例
      • 1.7.1 创建自定义自动配置
      • 1.7.2 注册自动配置
      • 1.7.3 项目结构
    • 1.8 SpringBoot 3.0与GraalVM集成
      • 1.8.1 GraalVM简介
      • 1.8.2 SpringBoot对GraalVM的支持挑战
      • 1.8.3 imports文件与GraalVM的兼容性
      • 1.8.4 SpringBoot AOT引擎
      • 1.8.5 GraalVM集成实例
      • 1.8.6 GraalVM集成的最佳实践
      • 1.8.7 GraalVM集成的限制和注意事项

1 SpringBoot 3 与 spring.factories

1.1 引言

SpringBoot 的演进过程中,3.0 版本带来了一次重大变革——取消了长期以来作为自动配置扩展机制核心的spring.factories文件。这个改变对于习惯了SpringBoot旧版本开发的工程师来说,需要了解新的机制和迁移策略。

1.2 spring.factories是什么

在讨论它的取消之前,我们首先需要理解 spring.factories文件在SpringBoot中扮演的角色。

1.2.1 基本概念

spring.factories是一个位于META-INF/目录下的配置文件,它基于 Java的SPI (Service Provider Interface) 机制的变种实现。这个文件的主要功能是允许开发者声明接口的实现类,从而实现SpringBoot 的自动装配和扩展点注册。

1.2.2 主要用途

SpringBoot 3.0之前,spring.factories文件有以下几个主要用途:

用途 描述 配置示例
自动配置类注册 声明自动配置类,使其在SpringBoot启动时自动加载 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.MyAutoConfiguration
初始化器注册 注册应用上下文初始化器 org.springframework.context.ApplicationContextInitializer=\
com.example.MyInitializer
监听器注册 注册应用事件监听器 org.springframework.context.ApplicationListener=\
com.example.MyListener
失败分析器注册 注册启动失败分析器 org.springframework.boot.diagnostics.FailureAnalyzer=\
com.example.MyFailureAnalyzer
环境后处理器 注册环境后处理器 org.springframework.boot.env.EnvironmentPostProcessor=\
com.example.MyEnvironmentPostProcessor

1.2.3 工作原理

SpringBoot 启动时,会使用SpringFactoriesLoader类扫描类路径下所有JAR包中的META-INF/spring.factories文件,读取配置信息并加载对应的类。这种机制使得SpringBoot能够以 约定优于配置 的方式实现自动装配。

// SpringFactoriesLoader核心代码示例(SpringBoot 2.x)
public final class SpringFactoriesLoader{// ...publicstatic <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader){// ... // 加载META-INF/spring.factories中的配置Map<String, List<String>> result = loadSpringFactories(classLoader);// ...}privatestatic Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {// 从类路径中加载所有META-INF/spring.factories文件// ...}// ...
}

1.3 为什么要取消spring.factories

SpringBoot 团队决定取消 spring.factories 机制有几个关键原因:

1.3.1 性能问题

spring.factories 机制需要在启动时扫描所有JAR包中的配置文件,当项目依赖较多时,这个过程会消耗大量时间,影响应用启动性能。

1.3.2 缺乏模块化支持

随着Java 9引入模块系统(JPMS),传统的基于类路径扫描的方式与模块化设计理念存在冲突。spring.factories无法很好地支持Java模块系统。

1.3.3 缺乏条件加载能力

spring.factories文件中的配置是静态的,无法根据条件动态决定是否加载某个实现。虽然可以在实现类上使用@Conditional 注解,但这种方式效率较低,因为所有类都会被加载到内存中进行条件评估。

1.3.4 配置分散难以管理

在大型项目中,spring.factories 配置分散在多个JAR包中,难以集中管理和查看全局配置。

1.3.5 GraalVM原生镜像支持

SpringBoot 3.0 的一个重要目标是提供对 GraalVM 原生镜像的一流支持。而 spring.factories 基于运行时类路径扫描的机制与 GraalVM 的提前编译(Ahead-of-Time Compilation, AOT)模型存在根本性冲突。具体来说:

  • 静态分析限制GraalVM 在构建原生镜像时需要静态分析代码,而spring.factories的类路径扫描是动态执行的,无法在构建时确定。
  • 反射使用问题spring.factories依赖于反射加载类,而GraalVM需要预先知道所有使用反射的类,这需要额外的配置和处理。
  • 资源访问限制: 在GraalVM原生镜像中,资源文件的访问机制与JVM有所不同,spring.factories文件的扫描方式需要特殊处理。

为了更好地支持GraalVMSpringBoot 需要一种在构建时就能确定的静态配置方式,而不是运行时的动态扫描。

1.4 替代方案 imports 文件

1.4.1 新机制介绍

SpringBoot 3.0开始,引入了基于imports文件的新机制,作为spring.factories的替代方案。这些文件位于META-INF/spring/目录下,每种类型的扩展点对应一个专门的文件:

文件名 对应的spring.factories中的键
org.springframework.boot.autoconfigure.AutoConfiguration.imports org.springframework.boot.autoconfigure.EnableAutoConfiguration
org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration.imports org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration

1.4.2 新机制优势

  • 更好的性能: 每种扩展点类型使用单独的文件,避免了加载不必要的配置
  • 支持Java模块系统: 新机制与Java模块系统兼容
  • 简化配置: 每行一个全限定类名,无需键值对形式,更易读易写
  • 更好的组织结构: 配置按功能分类到不同文件,结构更清晰

1.4.3 示例对比

旧方式(spring.factories):

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.FooAutoConfiguration,\
com.example.BarAutoConfiguration

新方式(AutoConfiguration.imports):

com.example.FooAutoConfiguration
com.example.BarAutoConfiguration

1.4.4 @AutoConfiguration

@AutoConfiguration(Spring Boot 3.0+ 自动配置),Spring Boot 自动配置 类,Spring Boot 3.0 以后替代 spring.factories 机制。
需要配合 META-INF/spring/ 下的 org.springframework.boot.autoconfigure.AutoConfiguration.imports 自动加载。
不会被 @ComponentScan 发现,但会被 Spring Boot 自动注册。

@AutoConfiguration 替代 @EnableAutoConfiguration
以前 Spring Boot 2.x 需要用 @EnableAutoConfiguration 来启用自动配置:

@SpringBootApplication
@EnableAutoConfiguration
public class MyApp {
}

Spring Boot 3.0 之后,@SpringBootApplication 已经隐式包含了 @EnableAutoConfiguration,所以不需要额外加。

注意:如果 @AutoConfiguration 没有被 AutoConfiguration.imports 声明,那么 Spring Boot 不会自动加载它,因为 @AutoConfiguration 不会被 @ComponentScan 扫描。

@AutoConfiguration 不能单独使用的原因

@AutoConfiguration 不会被 Spring 的组件扫描(@ComponentScan)发现。
它的作用是配合 AutoConfiguration.imports 文件,让 Spring Boot 在启动时自动加载它。
@AutoConfiguration 不会被 @SpringBootApplication 直接加载。
@SpringBootApplication 默认只会加载 @ComponentScan 能找到的 Bean,而 @AutoConfiguration 不在扫描范围内。

1.5 迁移指南

1.5.1 自动配置类迁移

将原来在 spring.factories 中注册的自动配置类移动到META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件中:

// 原来的自动配置类
@Configuration
@ConditionalOnXxx
public class MyAutoConfiguration{// ...
}// 在META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件中添加:
// com.example.MyAutoConfiguration

1.5.2 其他扩展点如何迁移

对于其他类型的扩展点,如果没有 imports 文件,SpringBoot 3.0 保留了 spring.factories 机制,但推荐在新项目中使用新的注册方式:

// 示例:注册ApplicationListener
// SpringBoot 3.0之前:在spring.factories中配置
// org.springframework.context.ApplicationListener=com.example.MyListener// SpringBoot 3.0之后:使用@Bean方式注册
@Configuration
public class MyConfiguration{@Beanpublic MyListener myListener(){returnnew MyListener();}
}

1.5.3 自定义扩展点迁移

对于自定义的扩展点,需要提供类似的imports文件机制:

// 自定义扩展点加载器示例
public class MyExtensionLoader{public List<MyExtension> loadExtensions(){return SpringFactoriesLoader.loadFactories(MyExtension.class, null);}
}// 迁移到新机制
public class MyExtensionLoader{public List<MyExtension> loadExtensions(){List<String> classNames = SpringFactoriesLoader.loadFactoryNames(MyExtension.class, null);// 或者实现自己的imports文件加载逻辑// ...}
}

1.6 SpringFactoriesLoader 变化

1.6.1 API变更

SpringBoot 3.0中,SpringFactoriesLoader类本身也经历了重大改变:

方法 SpringBoot 2.x SpringBoot 3.x
loadFactories 从spring.factories加载实例 保留但标记为过时
loadFactoryNames 从spring.factories加载类名 保留但标记为过时
新方法 - 引入新方法支持imports文件机制
// SpringBoot 3.x中新的SpringFactoriesLoader用法
public final class SpringFactoriesLoader{// 过时的方法@Deprecatedpublicstatic <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader){// ...}// 新方法publicstatic List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader){// 加载对应的imports文件// ...}// ...
}

1.6.2 兼容性考虑

为了保持向后兼容性,SpringBoot 3.0仍然支持通过spring.factories注册某些类型的扩展点,但新的项目应该优先考虑使用新机制。

1.7. 实战示例

1.7.1 创建自定义自动配置

下面是一个完整的示例,展示如何在SpringBoot 3.0中创建和注册自动配置:

// 1. 创建配置属性类
@ConfigurationProperties(prefix = "myapp")
public class MyProperties{private boolean enabled = true;private String name = "default";// getter和setter方法// ...
}// 2. 创建自动配置类
@AutoConfiguration// 注意这里使用了@AutoConfiguration而非@Configuration
@EnableConfigurationProperties(MyProperties.class)
@ConditionalOnProperty(prefix= "myapp", name = "enabled", havingValue = "true", matchIfMissing = true)
public class MyAutoConfiguration{ private final MyProperties properties;    public MyAutoConfiguration(MyProperties properties){this.properties = properties;}@Bean@ConditionalOnMissingBeanpublic MyService myService(){// 根据属性创建服务returnnew MyServiceImpl(properties.getName());}
}

1.7.2 注册自动配置

然后,在META-INF/spring/目录下创建org.springframework.boot.autoconfigure.AutoConfiguration.imports文件:

com.example.MyAutoConfiguration

1.7.3 项目结构

完整的项目结构如下:

myproject/
├── src/
│   └── main/
│       ├── java/
│       │   └── com/
│       │       └── example/
│       │           ├── MyProperties.java
│       │           ├── MyService.java
│       │           ├── MyServiceImpl.java
│       │           └── MyAutoConfiguration.java
│       └── resources/
│           └── META-INF/
│               └── spring/
│                   └── org.springframework.boot.autoconfigure.AutoConfiguration.imports
└── pom.xml

1.8 SpringBoot 3.0与GraalVM集成

SpringBoot 3.0GraalVM 的支持是取消 spring.factories 的主要原因之一。

1.8.1 GraalVM简介

GraalVM是一个高性能的JDK实现,它的一个重要特性是能够将Java应用编译成独立的原生可执行文件(Native Image)。这些原生镜像具有以下特点:

  • 快速启动: 启动时间通常在毫秒级,比传统JVM应用快10-100倍
  • 低内存占用: 内存占用显著降低,适合云原生和容器环境
  • 无需JVM: 可以独立运行,不需要Java运行时环境
  • 预先编译: 所有代码在构建时就编译为机器码,而非运行时编译

1.8.2 SpringBoot对GraalVM的支持挑战

SpringBoot框架面临的主要挑战是其动态特性与GraalVM静态分析模型之间的矛盾:

SpringBoot特性 与GraalVM的冲突 新机制如何解决
类路径扫描 动态扫描无法在构建时确定 静态的imports文件列出所有配置类
自动配置 依赖运行时条件评估 AOT处理阶段预先评估条件
动态代理 需要运行时生成 提前注册所有代理类
反射使用 GraalVM需要明确声明 优化反射使用并生成反射配置
资源加载 动态查找资源 清晰定义资源位置和加载方式

1.8.3 imports文件与GraalVM的兼容性

新的imports文件机制解决了与GraalVM集成的关键问题:

  • 静态可分析性: imports文件中明确列出所有配置类,可以在构建时静态分析
  • 路径明确性: 每种扩展点对应特定的文件路径,减少了运行时扫描
  • 更少的反射: imports文件的解析机制更简单,减少了对反射的依赖
  • 构建时处理: 可以在AOT编译阶段处理 imports 文件并生成相应的元数据

1.8.4 SpringBoot AOT引擎

为了更好地支持GraalVMSpringBoot 3.0 引入了一个新的 AOT引擎,它在构建时执行以下操作:

// SpringBoot 3.0 AOT处理示例
public class SpringAotProcessor{public void process(){// 1. 读取imports文件而非扫描spring.factoriesList<String> configurations = readImportsFiles();        // 2. 预先评估条件而非运行时评估List<String> effectiveConfigurations = evaluateConditions(configurations, buildTimeProperties);       // 3. 生成代理类而非运行时动态生成generateProxies(effectiveConfigurations);       // 4. 生成反射配置generateReflectionConfig(effectiveConfigurations);      // 5. 生成资源配置generateResourcesConfig();}
}

1.8.5 GraalVM集成实例

下面是一个完整的示例,展示如何在SpringBoot 3.0项目中配置和构建GraalVM原生镜像:

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.experimental</groupId><artifactId>spring-native</artifactId><version>${spring-native.version}</version></dependency>
</dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><image><builder>paketobuildpacks/builder:tiny</builder><env><BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE></env></image></configuration></plugin><plugin><groupId>org.springframework.experimental</groupId><artifactId>spring-aot-maven-plugin</artifactId><executions><execution><id>generate</id><goals><goal>generate</goal></goals></execution></executions></plugin></plugins>
</build>

自动配置迁移示例

// 旧的方式 - spring.factories配置:
// META-INF/spring.factories:
// org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.MyNativeCompatibleConfig// 新的方式 - imports文件:
// META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports:
// com.example.MyNativeCompatibleConfig// 自动配置类
@AutoConfiguration
@NativeHint(options = "--enable-url-protocols=http") // GraalVM特定的提示
publicclassMyNativeCompatibleConfig{@Beanpublic MyService myService(){returnnew MyNativeCompatibleService();}
}

1.8.6 GraalVM集成的最佳实践

  • 减少反射使用: 尽量使用构造函数注入而非字段注入
  • 避免动态代理: 减少使用需要动态代理的特性
  • 静态初始化: 在构建时初始化静态数据而非运行时
  • 使用imports文件: 确保所有配置类都通过imports文件注册
  • 添加必要的提示: 使用@NativeHint等注解提供GraalVM所需的提示

1.8.7 GraalVM集成的限制和注意事项

  • 动态特性受限: 诸如运行时生成字节码、动态加载类等特性在原生镜像中受限
  • 反射使用: 必须明确声明使用反射的类
  • 构建时间: 原生镜像构建时间较长,需要合理规划CI/CD流程
  • 调试复杂度: 原生镜像的调试比传统JVM更复杂
  • 第三方库兼容性: 某些依赖可能尚未针对GraalVM优化

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

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

相关文章

20242924 2024-2025-2 《网络攻防实践》实践四报告

20242924 2024-2025-2 《网络攻防实践》实践4报告 1.实践内容 在网络攻防实验环境中完成TCP/IP协议栈重点协议的攻击实验,包括ARP缓存欺骗攻击、ICMP重定向攻击、SYN Flood攻击、TCP RST攻击、TCP会话劫持攻击。 1.1 ARP缓存欺骗攻击 ARP(Address Resolution Protocol):即地…

TCPing:用于测试禁ping设备的 TCP 连接的可达性

场景: 当设备被禁止 icmp 协议通过时,可以使用 tcp 来测试网络的连通性以及延迟TCPing tcping 是一个类似于ping 的工具,但它用于测试 TCP 连接的可达性,而不仅仅是 ICMP(传统的 ping)连接。它通过尝试与远程主机上的指定端口建立 TCP 连接,来判断目标主机的端口是否开放…

Go打包dll

1. goland 编写 代码package main/* #include <stdlib.h> */ import "C"//export Add func Add(a, b int) int {return a + b }//export Greet func Greet(v *C.char) *C.char {name := C.GoString(v)return C.CString("Hello from !" + name) } //e…

SpringSecurity5(11-跨域配置)

Spring Security跨域配置通过在WebSecurityConfigurerAdapter中自定义CorsConfiguration实现。可以配置允许的请求来源、请求方法、请求头等,以确保前端与后端之间的安全通信。结合@CrossOrigin注解或CorsFilter进行灵活配置,保障不同源的资源访问权限控制和数据安全。Spring…

AI大模型危机-多维对抗时代的网络安全与隐私困局

文章学习笔记:AI大模型危机-多维对抗时代的网络安全与隐私困局AI发展引发的安全风险与日俱增,加剧了网络安全形式的复杂性。本文剖析了大模型全生命周期安全威胁图谱,揭示数据资源枯竭、隐私泄露、监管滞后等风险问题。 AI大模型驱动网络安全风险”多维裂变“ AI大模型的发展…

CB2401是一款高性能、低功耗可完美替代RFX2401C/AT2401C

CB2401是一款高性能、低功耗的2.4GHz射频前端集成电路(RFIC),专为蓝牙、Zigbee和2.4GHz专有协议无线应用设计。该芯片可完美替代RFX2401C和AT2401C,提供更优的性能和更低的功耗。 主要技术参数核心优势 更高发射功率:+22dBm输出功率,比竞品高出2dB,通信距离更远 更低功耗:…

CH341 Linux驱动 设备驱动连接后又自动关闭

1.uname -r 查看linux版本号2.选择合适内核下的驱动 https://elixir.bootlin.com/linux/v6.8/source/drivers/usb/serial/ch341.c 3.make & make install 4.发现/dev下没有ttyUSB0设备 5.dmesg 发现问题 6043.015612] usb 1-4.2: ch341-uart converter now attached to tty…

会计学-现金流表(三)

介绍 什么是现金流量表?:就是反映你当前现金状况的一个表。 由四个部分构成:期初现金 + 本期流入现金 - 本期流出现金 = 期末现金一个企业或者一个家庭很多时候,即使是落入资不抵债的局面,也未必就会陷入破产,只要他的现金流可以一直维持日常运转,就可以不需要被迫去变卖…

会计学-资产负债表(二)

介绍 什么是资产负债表?:反映的是一家公司或者是一个家庭在某一时刻体量的表。 公式是:资产 = 负债 + 所有者权益。 资产负债表分成左右两栏,左边是资产,右边是负债和权益。 资产表示你的家底有多厚,负债和权益表示这些家底是从哪里来的。会计学上的资产负债表比较复杂的…

王炸!Kafka 4.0 重磅发布,Java 8 和 Zookeeper 彻底被抛弃了,一个时代结束了!

大家好,我是R哥。 Kafka 4.0 终于来了!这次更新可不只是常规的版本优化,而是一次重大架构调整,尤其是不再支持 Java 8,彻底移除了 Zookeeper,正式启用 KRaft 模式,让 Kafka 变得更加独立、高效。 除了这个重磅更新外,Kafka 4.0 还砍掉了一些历史包袱,还带来几个重磅新…

会计学-开篇(一)

介绍 是不是觉得会计学极其枯燥,死板,无趣?因为会计准则本身就是人为设定的游戏规则,整个学习过程感觉就是一直在背诵各种条条框框,完全没有创造性。相比之下,不管是数学,编程,物理学,生物学,投资学,都显得很有意思,这些学科都是帮助我们通向创造。而会计学似乎只会…