Why Spring Boot 3 Removed spring.factories & How to Migrate to imports
This article explains the removal of the spring.factories file in Spring Boot 3, the performance and modularity reasons behind it, introduces the new imports‑based mechanism, provides step‑by‑step migration guidance, and shows how the change improves GraalVM native image support.
1. Introduction
Spring Boot 3.0 introduces a major change by removing the spring.factories file, which was the core of auto‑configuration and extension registration in earlier versions. Understanding the new mechanism and migration strategy is essential for developers upgrading their projects.
This article explores the reasons for the change, its impact, and the replacement approach.
2. What is spring.factories?
The spring.factories file resides under META-INF/ and uses a variant of Java’s SPI mechanism to declare implementations of interfaces, enabling automatic configuration and extension point registration in Spring Boot.
2.1 Basic concept
spring.factoriesis a configuration file located in META-INF/. It lists key‑value pairs where the key is an extension point interface and the value is a comma‑separated list of implementation class names.
2.2 Main uses (pre‑3.0)
Auto‑configuration registration
ApplicationListener registration
Other extension point declarations
2.3 How it works
During startup, SpringFactoriesLoader scans all JARs for META-INF/spring.factories, reads the entries and loads the corresponding classes, following a “convention over configuration” approach.
public final class SpringFactoriesLoader {
@Deprecated
public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
// ...
}
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
// Load corresponding imports file
// ...
}
}3. Why spring.factories was removed
3.1 Performance issues
Scanning every JAR for spring.factories becomes costly in projects with many dependencies, slowing application startup.
3.2 Lack of modularity support
Java 9’s module system (JPMS) conflicts with class‑path scanning, making spring.factories unsuitable for modular applications.
3.3 No conditional loading
Entries are static; they cannot be loaded conditionally at runtime without loading all classes first, which reduces efficiency.
3.4 Distributed configuration
In large projects, configurations are scattered across many JARs, making global management difficult.
3.5 GraalVM native image support
Static analysis limitation: GraalVM needs compile‑time knowledge of classes, but spring.factories relies on dynamic scanning.
Reflection usage: the mechanism depends on reflection, which requires explicit configuration for native images.
Resource access: native images handle resources differently, so the traditional scanning approach does not work.
Therefore, a static, build‑time configuration method is required.
4. Alternative: imports files
4.1 New mechanism
Since Spring Boot 3.0, an imports file under META-INF/spring/ replaces spring.factories. Each extension point type has its own file, e.g.,
org.springframework.boot.autoconfigure.AutoConfiguration.imports.
4.2 Advantages
Better performance : Separate files avoid loading unnecessary configurations.
Java module compatibility : Works seamlessly with JPMS.
Simplified configuration : One fully‑qualified class name per line, no key‑value syntax.
Clearer organization : Configurations are grouped by functionality.
4.3 Example comparison
Old spring.factories entry:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.FooAutoConfiguration,\
com.example.BarAutoConfigurationNew AutoConfiguration.imports entry:
com.example.FooAutoConfiguration
com.example.BarAutoConfiguration5. Migration guide
5.1 Auto‑configuration classes
Move entries from spring.factories to
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports:
// Original auto‑configuration class
@Configuration
@ConditionalOnXxx
public class MyAutoConfiguration {
// ...
}
// Add the fully‑qualified name to the imports file
// com.example.MyAutoConfiguration5.2 Other extension points
For non‑auto‑configuration extension points, keep using spring.factories if necessary, but prefer the new registration style in new projects.
// Register ApplicationListener (old way)
org.springframework.context.ApplicationListener=com.example.MyListener
// New way (bean registration)
@Configuration
public class MyConfiguration {
@Bean
public MyListener myListener() {
return new MyListener();
}
}5.3 Custom extension loaders
public class MyExtensionLoader {
public List<MyExtension> loadExtensions() {
return SpringFactoriesLoader.loadFactories(MyExtension.class, null);
}
}
// After migration, load from imports file instead
public class MyExtensionLoader {
public List<MyExtension> loadExtensions() {
List<String> classNames = SpringFactoriesLoader.loadFactoryNames(MyExtension.class, null);
// custom loading logic
}
}6. Changes to SpringFactoriesLoader
6.1 API updates
The old loadFactories method is deprecated; the new loadFactoryNames reads the imports files directly.
6.2 Compatibility
Spring Boot 3.0 still supports spring.factories for backward compatibility, but new projects should adopt the imports mechanism.
7. Practical example
7.1 Custom auto‑configuration
@ConfigurationProperties(prefix = "myapp")
public class MyProperties {
private boolean enabled = true;
private String name = "default";
// getters and setters
}
@AutoConfiguration
@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
@ConditionalOnMissingBean
public MyService myService() {
return new MyServiceImpl(properties.getName());
}
}7.2 Register the auto‑configuration
Create
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.importscontaining:
com.example.MyAutoConfiguration7.3 Project structure
myproject/
├── src/main/java/com/example/
│ ├── MyProperties.java
│ ├── MyService.java
│ ├── MyServiceImpl.java
│ └── MyAutoConfiguration.java
├── src/main/resources/META-INF/spring/
│ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports
└── pom.xml8. Performance comparison
A typical medium‑size Spring Boot application shows noticeable startup time reduction when using the new imports mechanism. (Image omitted for brevity.)
Note: Actual gains depend on project size and structure.
9. FAQ
9.1 Compatibility issues
Existing libraries that still use spring.factories continue to work because Spring Boot retains support for the old file.
9.2 Migration effort
Large projects can migrate incrementally: start with auto‑configuration classes, then move other extension points.
9.3 Custom loaders
Implement a similar imports‑based loader for custom extension points, following the new SpringFactoriesLoader.loadFactoryNames pattern.
10. Spring Boot 3.0 and GraalVM integration
10.1 Why the change helps GraalVM
GraalVM requires static analysis; the imports files provide a deterministic list of configuration classes, eliminating dynamic class‑path scanning and reducing reflection usage.
10.2 Benefits for GraalVM
Static analyzability : All configuration classes are listed explicitly.
Clear paths : Each extension point has a dedicated file.
Less reflection : Simpler parsing reduces the need for reflective access.
Build‑time handling : Imports can be processed during AOT compilation.
10.3 AOT engine example
public class SpringAotProcessor {
public void process() {
List<String> configurations = readImportsFiles();
List<String> effectiveConfigurations = evaluateConditions(configurations, buildTimeProperties);
generateProxies(effectiveConfigurations);
generateReflectionConfig(effectiveConfigurations);
generateResourcesConfig();
}
}10.4 GraalVM native image configuration
Example Maven setup for building a native image with Spring Native and the new imports mechanism.
<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>10.5 Best practices for GraalVM
Minimize reflection; prefer constructor injection.
Avoid dynamic proxies when possible.
Initialize static data at build time.
Register all configuration classes via imports files.
Use @NativeHint to supply GraalVM hints.
10.6 Limitations
Dynamic features (runtime bytecode generation, class loading) are restricted.
Reflection requires explicit configuration.
Native image builds are slower; plan CI/CD accordingly.
Debugging native images is more complex than JVM debugging.
Some third‑party libraries may lack GraalVM support.
By removing spring.factories and adopting the imports mechanism, Spring Boot 3.0 greatly improves GraalVM native image integration, enabling developers to build high‑performance, low‑latency cloud‑native applications.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
macrozheng
Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
