Spring Boot
auto-configuration
spring.factories
annotations
Java programming

Why use spring.factories for Spring Boot auto-configuration instead of annotations?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Spring Boot auto-configuration was historically discovered through metadata files such as spring.factories instead of relying on component scanning alone. The reason is control: auto-configuration has to be found from dependency jars before normal application beans are created, and it has to load in a deterministic order. The important design point is not the filename itself, but the fact that Boot uses explicit registration metadata rather than hoping annotations will be discovered by scanning.

Why Annotation Scanning Alone Is Not Enough

A plain annotation such as @Configuration only helps after Spring has already decided what to scan. That works for application code, but auto-configuration lives in external starter jars that the application did not explicitly scan.

If Boot depended only on component scanning, every starter would need to be included in scan base packages, which would be fragile, slow, and easy to misconfigure.

With metadata registration, Boot can ask:

  • which auto-configurations exist
  • in which order they should be considered
  • which conditions decide whether they activate

That is much more deterministic than annotation discovery.

What spring.factories Historically Did

Historically, a starter jar registered its auto-configuration classes in META-INF/spring.factories. Boot read that metadata early and imported the listed configuration classes conditionally.

Example auto-configuration:

java
1package com.example.demo;
2
3import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
4import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
5import org.springframework.context.annotation.Bean;
6import org.springframework.context.annotation.Configuration;
7
8@Configuration
9@ConditionalOnClass(MyClient.class)
10public class MyClientAutoConfiguration {
11
12    @Bean
13    @ConditionalOnMissingBean
14    public MyClient myClient() {
15        return new MyClient("default");
16    }
17}

And the metadata entry:

properties
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.demo.MyClientAutoConfiguration

That explicit registration step is what made the class discoverable as auto-configuration rather than ordinary application configuration.

Why Explicit Metadata Is Better for Starters

There are several practical advantages:

  1. Starter jars stay decoupled from application package scanning.
  2. Boot can evaluate auto-configurations in a predictable import phase.
  3. Startup remains faster than broad classpath scanning.
  4. Library authors can publish plug-in style configuration cleanly.

This is particularly important for third-party starters. A reusable starter should work when added as a dependency, not only when the consumer manually extends scan rules.

Conditions Belong with the Configuration Class

The metadata file answers discovery. The annotations answer activation. Those are different concerns.

The configuration class still uses conditional annotations such as:

  • '@ConditionalOnClass'
  • '@ConditionalOnMissingBean'
  • '@ConditionalOnProperty'

That means Boot first discovers the class from metadata, then decides whether it should actually contribute beans.

Why This Is Cleaner Than Forcing @ComponentScan

Using @ComponentScan for starters would blur the line between library internals and public auto-configuration API. It would also make ordering harder and create more accidental beans.

Auto-configuration should be opt-in by dependency and condition, not opt-in by scanning arbitrary package trees.

Modern Boot Context

In newer Spring Boot generations, the registration mechanism evolved, but the architectural reason stayed the same: explicit metadata-driven discovery is preferred over annotation scanning for framework-level auto-configuration. So even if a project is no longer centered on spring.factories, the answer to the design question is still metadata, ordering, and controlled discovery.

Small Starter Example

If you were building a starter, the structure would look like this:

java
1public class MyClient {
2    private final String mode;
3
4    public MyClient(String mode) {
5        this.mode = mode;
6    }
7
8    public String getMode() {
9        return mode;
10    }
11}

Consumer code does not scan your starter package. It just adds the dependency, and Boot discovers the configuration through metadata.

Common Pitfalls

  • Assuming @Configuration alone makes a class an auto-configuration candidate.
  • Confusing class discovery with activation conditions.
  • Using component scanning in starter jars and leaking unintended beans.
  • Ignoring ordering concerns between multiple auto-configurations.
  • Forgetting that framework infrastructure and application scanning solve different problems.

Summary

  • Auto-configuration needs explicit discovery from dependency jars, not normal package scanning.
  • 'spring.factories historically provided that early metadata-driven registration.'
  • Conditional annotations still control whether discovered configuration activates.
  • Metadata-based discovery is faster, more deterministic, and cleaner for starter libraries.
  • Even in newer Boot setups, the design rationale remains the same.

Course illustration
Course illustration

All Rights Reserved.