釣友寶 (微信小程序):一款專門為 釣友 開發(fā)的 免費的 分享釣點地圖與實時天氣的軟件,地圖中標記了所有野釣、釣場、公共水域等的精確位置,支持導航、 預測釣魚位置的魚情 等功能。

SpringBoot 3.0之后為什么取消了spring.factories

1. 引言

在SpringBoot的演進過程中,3.0版本帶來了一次重大變革——取消了長期以來作為自動配置和擴展機制核心的spring.factories文件。這個改變對于習慣了SpringBoot舊版本開發(fā)的工程師來說,需要了解新的機制和遷移策略。

本文將深入探討這一變更的原因、影響以及替代方案。

2. spring.factories是什么

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

2.1 基本概念

spring.factories是一個位于META-INF/目錄下的配置文件,它基于Java的SPI(Service Provider Interface)機制的變種實現(xiàn)。這個文件的主要功能是允許開發(fā)者聲明接口的實現(xiàn)類,從而實現(xiàn)SpringBoot的自動裝配和擴展點注冊。

2.2 主要用途

在SpringBoot 3.0之前,spring.factories文件有以下幾個主要用途:

打開網(wǎng)易新聞 查看精彩圖片

2.3 工作原理

SpringBoot啟動時,會使用SpringFactoriesLoader類掃描類路徑下所有JAR包中的META-INF/spring.factories文件,讀取配置信息并加載對應的類。這種機制使得SpringBoot能夠以"約定優(yōu)于配置"的方式實現(xiàn)自動裝配。

// SpringFactoriesLoader核心代碼示例(SpringBoot 2.x) publicfinalclass SpringFactoriesLoader {     // ...     publicstatic    List   loadFactories(Class factoryType, @Nullable ClassLoader classLoader)  {         // ...          // 加載META-INF/spring.factories中的配置         Map > result = loadSpringFactories(classLoader);         // ...     }          privatestatic Map > loadSpringFactories( @Nullable ClassLoader classLoader) {         // 從類路徑中加載所有META-INF/spring.factories文件         // ...     }     // ... }

3. 為什么要取消spring.factories

SpringBoot團隊決定取消spring.factories機制有幾個關鍵原因:

3.1 性能問題

spring.factories機制需要在啟動時掃描所有JAR包中的配置文件,當項目依賴較多時,這個過程會消耗大量時間,影響應用啟動性能。

3.2 缺乏模塊化支持

隨著Java 9引入模塊系統(tǒng)(JPMS),傳統(tǒng)的基于類路徑掃描的方式與模塊化設計理念存在沖突。spring.factories無法很好地支持Java模塊系統(tǒng)。

3.3 缺乏條件加載能力

spring.factories文件中的配置是靜態(tài)的,無法根據(jù)條件動態(tài)決定是否加載某個實現(xiàn)。雖然可以在實現(xiàn)類上使用@Conditional注解,但這種方式效率較低,因為所有類都會被加載到內(nèi)存中進行條件評估。

3.4 配置分散難以管理

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

3.5 GraalVM原生鏡像支持

SpringBoot 3.0的一個重要目標是提供對GraalVM原生鏡像的一流支持。而spring.factories基于運行時類路徑掃描的機制與GraalVM的提前編譯(Ahead-of-Time Compilation, AOT)模型存在根本性沖突。具體來說:

  • 靜態(tài)分析限制:GraalVM在構建原生鏡像時需要靜態(tài)分析代碼,而spring.factories的類路徑掃描是動態(tài)執(zhí)行的,無法在構建時確定。

  • 反射使用問題:spring.factories依賴于反射加載類,而GraalVM需要預先知道所有使用反射的類,這需要額外的配置和處理。

  • 資源訪問限制:在GraalVM原生鏡像中,資源文件的訪問機制與JVM有所不同,spring.factories文件的掃描方式需要特殊處理。

為了更好地支持GraalVM,SpringBoot需要一種在構建時就能確定的靜態(tài)配置方式,而不是運行時的動態(tài)掃描。

4. 替代方案:imports文件

4.1 新機制介紹

從SpringBoot 3.0開始,引入了基于imports文件的新機制,作為spring.factories的替代方案。這些文件位于META-INF/spring/目錄下,每種類型的擴展點對應一個專門的文件:

打開網(wǎng)易新聞 查看精彩圖片

4.2 新機制優(yōu)勢

  • 更好的性能:每種擴展點類型使用單獨的文件,避免了加載不必要的配置

  • 支持Java模塊系統(tǒng):新機制與Java模塊系統(tǒng)兼容

  • 簡化配置:每行一個全限定類名,無需鍵值對形式,更易讀易寫

  • 更好的組織結構:配置按功能分類到不同文件,結構更清晰

4.3 示例對比

舊方式(spring.factories):

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

新方式(AutoConfiguration.imports):

com.example.FooAutoConfiguration com.example.BarAutoConfiguration

5. 遷移指南

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

5.2 其他擴展點如何遷移

對于其他類型的擴展點,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 {     @Bean     public MyListener myListener() {         return new MyListener();     } }

5.3 自定義擴展點遷移

對于自定義的擴展點,需要提供類似的imports文件機制:

// 自定義擴展點加載器示例 publicclass MyExtensionLoader {     public List   loadExtensions() {         return SpringFactoriesLoader.loadFactories(MyExtension.class, null);     } } // 遷移到新機制 publicclass MyExtensionLoader {     public List   loadExtensions() {         List classNames = SpringFactoriesLoader.loadFactoryNames(             MyExtension.class, null);         // 或者實現(xiàn)自己的imports文件加載邏輯         // ...     } }

6. SpringFactoriesLoader的變化

6.1 API變更

在SpringBoot 3.0中,SpringFactoriesLoader類本身也經(jīng)歷了重大改變:

打開網(wǎng)易新聞 查看精彩圖片
// SpringBoot 3.x中新的SpringFactoriesLoader用法 publicfinalclass SpringFactoriesLoader {     // 過時的方法     @Deprecated     publicstatic    List   loadFactories(Class factoryType, @Nullable ClassLoader classLoader)  {         // ...     }          // 新方法     public static List   loadFactoryNames(Class factoryType, @Nullable ClassLoader classLoader) {         // 加載對應的imports文件         // ...     }          // ... }

6.2 兼容性考慮

為了保持向后兼容性,SpringBoot 3.0仍然支持通過spring.factories注冊某些類型的擴展點,但新的項目應該優(yōu)先考慮使用新機制。

7. 實戰(zhàn)示例

7.1 創(chuàng)建自定義自動配置

下面是一個完整的示例,展示如何在SpringBoot 3.0中創(chuàng)建和注冊自動配置:

// 1. 創(chuàng)建配置屬性類 @ConfigurationProperties(prefix = "myapp") publicclass MyProperties {     privateboolean enabled = true;     private String name = "default";          // getter和setter方法     // ... } // 2. 創(chuàng)建自動配置類 @AutoConfiguration// 注意這里使用了@AutoConfiguration而非@Configuration @EnableConfigurationProperties(MyProperties.class) @ConditionalOnProperty(prefix = "myapp", name = "enabled", havingValue = "true", matchIfMissing = true) publicclass MyAutoConfiguration {          privatefinal MyProperties properties;          public MyAutoConfiguration(MyProperties properties) {         this.properties = properties;     }          @Bean     @ConditionalOnMissingBean     public MyService myService() {         // 根據(jù)屬性創(chuàng)建服務         returnnew MyServiceImpl(properties.getName());     } }

7.2 注冊自動配置

然后,在META-INF/spring/目錄下創(chuàng)建org.springframework.boot.autoconfigure.AutoConfiguration.imports文件:

com.example.MyAutoConfiguration

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

8. 性能對比

在一個典型的中型SpringBoot應用中,使用新機制后的啟動性能對比:

打開網(wǎng)易新聞 查看精彩圖片

注:實際性能提升取決于項目規(guī)模和結構

9. 常見問題與解決方案

9.1 兼容性問題

問題:現(xiàn)有的依賴庫仍使用spring.factories,會有兼容問題嗎?

解決方案:SpringBoot 3.0保留了對spring.factories的支持,舊的庫仍然可以正常工作。但新的代碼應該使用新機制。

9.2 遷移困難

問題:大型項目遷移到新機制工作量大

解決方案:可以分階段遷移,先遷移自動配置類,再逐步遷移其他擴展點。

9.3 自定義加載器

問題:自定義的SpringFactoriesLoader使用者如何遷移?

解決方案:參考SpringBoot的新實現(xiàn),為自定義擴展點提供類似的imports文件加載機制。

10. SpringBoot 3.0與GraalVM集成

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

10.1 GraalVM簡介

GraalVM是一個高性能的JDK實現(xiàn),它的一個重要特性是能夠將Java應用編譯成獨立的原生可執(zhí)行文件(Native Image)。這些原生鏡像具有以下特點:

  • 快速啟動:啟動時間通常在毫秒級,比傳統(tǒng)JVM應用快10-100倍

  • 低內(nèi)存占用:內(nèi)存占用顯著降低,適合云原生和容器環(huán)境

  • 無需JVM:可以獨立運行,不需要Java運行時環(huán)境

  • 預先編譯:所有代碼在構建時就編譯為機器碼,而非運行時編譯

10.2 SpringBoot對GraalVM的支持挑戰(zhàn)

SpringBoot框架面臨的主要挑戰(zhàn)是其動態(tài)特性與GraalVM靜態(tài)分析模型之間的矛盾:

打開網(wǎng)易新聞 查看精彩圖片

10.3 imports文件與GraalVM的兼容性

新的imports文件機制解決了與GraalVM集成的關鍵問題:

  • 靜態(tài)可分析性:imports文件中明確列出所有配置類,可以在構建時靜態(tài)分析

  • 路徑明確性:每種擴展點對應特定的文件路徑,減少了運行時掃描

  • 更少的反射:imports文件的解析機制更簡單,減少了對反射的依賴

  • 構建時處理:可以在AOT編譯階段處理imports文件并生成相應的元數(shù)據(jù)

10.4 SpringBoot AOT引擎

為了更好地支持GraalVM,SpringBoot 3.0引入了一個新的AOT引擎,它在構建時執(zhí)行以下操作:

// SpringBoot 3.0 AOT處理示例 publicclass SpringAotProcessor {     public void process() {         // 1. 讀取imports文件而非掃描spring.factories         List configurations = readImportsFiles();                  // 2. 預先評估條件而非運行時評估         List effectiveConfigurations =              evaluateConditions(configurations, buildTimeProperties);                  // 3. 生成代理類而非運行時動態(tài)生成         generateProxies(effectiveConfigurations);                  // 4. 生成反射配置         generateReflectionConfig(effectiveConfigurations);                  // 5. 生成資源配置         generateResourcesConfig();     } }

10.5 GraalVM集成實例

下面是一個完整的示例,展示如何在SpringBoot 3.0項目中配置和構建GraalVM原生鏡像:

Maven配置


     

         

 org.springframework.boot groupId>         

 spring-boot-starter artifactId>      dependency>     

         

 org.springframework.experimental groupId>         

 spring-native artifactId>         

 ${spring-native.version} version>      dependency> dependencies> 

     

         

             

 org.springframework.boot groupId>             

 spring-boot-maven-plugin artifactId>             

                 


 paketobuildpacks/builder:tiny builder>                     

                         

 true BP_NATIVE_IMAGE>                      env>                  image>              configuration>          plugin>         

             

 org.springframework.experimental groupId>             

 spring-aot-maven-plugin artifactId>             

                 

                     

 generate id>                     

                         

 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特定的提示 publicclass MyNativeCompatibleConfig {     @Bean     public MyService myService() {         returnnew MyNativeCompatibleService();     } }

10.6 性能對比:傳統(tǒng)JVM vs GraalVM原生鏡像

使用新的imports機制后,SpringBoot應用在GraalVM原生鏡像中的性能表現(xiàn):

打開網(wǎng)易新聞 查看精彩圖片

10.7 GraalVM集成的最佳實踐

  • 減少反射使用:盡量使用構造函數(shù)注入而非字段注入

  • 避免動態(tài)代理:減少使用需要動態(tài)代理的特性

  • 靜態(tài)初始化:在構建時初始化靜態(tài)數(shù)據(jù)而非運行時

  • 使用imports文件:確保所有配置類都通過imports文件注冊

  • 添加必要的提示:使用@NativeHint等注解提供GraalVM所需的提示

10.8 GraalVM集成的限制和注意事項

  • 動態(tài)特性受限:諸如運行時生成字節(jié)碼、動態(tài)加載類等特性在原生鏡像中受限

  • 反射使用:必須明確聲明使用反射的類

  • 構建時間:原生鏡像構建時間較長,需要合理規(guī)劃CI/CD流程

  • 調試復雜度:原生鏡像的調試比傳統(tǒng)JVM更復雜

  • 第三方庫兼容性:某些依賴可能尚未針對GraalVM優(yōu)化

通過取消spring.factories并引入新的imports文件機制,SpringBoot 3.0顯著改善了與GraalVM的集成體驗,讓開發(fā)者能夠更容易地構建高性能、低延遲的云原生應用。

Java精選面試題 (微信小程序):5000+道面試題和選擇題,包含Java基礎、MQ、Redis、SpringBoot、Elasticsearch、Docker、K8s、Flink、Spark、架構設計、大廠真題等,在線隨時刷題!
來源:https://blog.csdn.net/AWen_Jack/article/details/146455281

公眾號“Java精選”所發(fā)表內(nèi)容注明來源的,版權歸原出處所有(無法查證版權的或者未注明出處的均來自網(wǎng)絡,系轉載,轉載的目的在于傳遞更多信息,版權屬于原作者。如有侵權,請聯(lián)系,筆者會第一時間刪除處理!

最近有很多人問,有沒有讀者或者摸魚交流群!加入方式很簡單,公眾號Java精選,回復“加群”,即可入群!

文章有幫助的話,點在看,轉發(fā)吧!