Why SpringBoot Replaced spring.factories with a Faster, GraalVM‑Friendly Imports Mechanism

SpringBoot 3.0 drops the long‑standing spring.factories file in favor of per‑extension imports files, eliminating full‑classpath scans, improving startup speed, aligning with Java modules, and enabling seamless GraalVM native‑image support, while providing a clear migration path for existing projects.

Java Companion
Java Companion
Java Companion
Why SpringBoot Replaced spring.factories with a Faster, GraalVM‑Friendly Imports Mechanism

Background: the role of spring.factories

In SpringBoot 2.x, spring.factories was a Java‑SPI‑style configuration file placed under META-INF/. It listed key‑value pairs that told SpringBoot which implementation classes to load for a given extension point, enabling the framework’s “convention over configuration” approach.

How spring.factories worked

At application start‑up, SpringFactoriesLoader scanned every JAR on the classpath, read all META-INF/spring.factories files, parsed the entries, and loaded the corresponding classes. This mechanism supported automatic configuration, ApplicationListener registration, and third‑party component integration.

Why spring.factories became a liability

Slow startup : The loader scans every JAR regardless of whether the entries are needed, causing noticeable CPU and I/O overhead in large microservice projects.

Incompatible with Java 9+ modules : The runtime‑only class‑path scanning conflicts with JPMS’s explicit dependency declarations, making module boundaries unclear.

Eager loading then filtering : All classes are loaded before @Conditional checks can discard unsuitable ones, wasting resources.

Scattered configuration : Each dependency may contain its own spring.factories, turning configuration into a set of hidden “paper notes” that are hard to locate and prone to conflicts.

GraalVM native‑image incompatibility : The runtime scanning prevents static analysis, requires extensive reflection configuration, and mismatches resource‑access patterns, blocking native‑image builds.

New solution: imports files

SpringBoot 3.0 introduces a set of .imports files under META-INF/spring/. Each extension point gets its own file (e.g., AutoConfiguration.imports, ApplicationContextInitializer.imports, FailureAnalyzer.imports) containing one fully‑qualified class name per line.

Four main advantages

Faster startup : Only the files relevant to the required extension points are read, eliminating the full‑classpath scan.

Module‑system friendly : The META-INF/spring directory can be opened via JPMS opens declarations, satisfying explicit module dependencies.

Simpler configuration : The key‑value syntax disappears; developers write a plain list of class names, reducing formatting errors.

Native‑image ready : The static file structure allows GraalVM’s build‑time analysis to determine all required classes without extra reflection metadata.

Side‑by‑side comparison

Old spring.factories entry (auto‑configuration):

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

New AutoConfiguration.imports file:

com.example.FooAutoConfiguration
com.example.BarAutoConfiguration

Migration guide for auto‑configuration

Leave existing annotations ( @Configuration, @ConditionalOn…) unchanged.

Create the directory resources/META-INF/spring/ and add a file named

org.springframework.boot.autoconfigure.AutoConfiguration.imports

.

List each auto‑configuration class on its own line, e.g., com.example.FooAutoConfiguration.

Other extension points

For ApplicationListener, FailureAnalyzer, etc., you can either create the corresponding .imports file (recommended) or register the bean manually with @Bean in a configuration class.

Example using an imports file

com.example.MyApplicationListener

Example using @Bean

@Configuration
public class ListenerConfig {
    @Bean
    public MyApplicationListener myApplicationListener() {
        return new MyApplicationListener();
    }
}

Custom loader migration

Old loader using SpringFactoriesLoader:

public class MyExtensionLoader {
    public List<MyExtension> loadExtensions() {
        // Load implementations from spring.factories
        return SpringFactoriesLoader.loadFactories(MyExtension.class, null);
    }
}

New loader reading a custom .imports file:

public class MyExtensionLoader {
    public List<MyExtension> loadExtensions() {
        List<String> classNames = loadFromImportsFile("META-INF/spring/MyExtension.imports");
        return classNames.stream()
            .map(className -> ClassUtils.forName(className, null).newInstance())
            .collect(Collectors.toList());
    }
}

SpringFactoriesLoader API changes

The original loadFactories method is now deprecated. A new method loadFactoryNames returns only class names, leaving instantiation to the caller for greater flexibility.

Compatibility notes

For backward compatibility, SpringBoot 3.0 still parses spring.factories for core extension points like EnableAutoConfiguration. However, new projects should adopt the imports files, and future releases will drop the compatibility layer.

Conclusion

The retirement of spring.factories is not a judgment of its past usefulness but a response to evolving requirements: performance, JPMS compatibility, and GraalVM native‑image support. The imports mechanism delivers a static, categorized, and efficient configuration model that aligns SpringBoot with the cloud‑native era.

MigrationSpringBootGraalVMauto-configurationjava-modulesimportsSpring Factories
Java Companion
Written by

Java Companion

A highly professional Java public account

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.