Intermediate Reading time: ~8 min

Auto-configuration

@EnableAutoConfiguration, conditional beans, spring.factories, auto-config internals

Auto-configuration

1. Definition

The Spring Boot auto-configuration mechanism automatically configures application Spring beans based on libraries on the classpath, existing bean definitions, and properties. The @EnableAutoConfiguration annotation (part of @SpringBootApplication) activates this system. The essence of auto-configuration is that developers should not need to manually configure what the framework can infer — the "convention over configuration" principle.

For example, if spring-boot-starter-data-jpa and an H2 driver are on the classpath, Spring Boot automatically creates a DataSource, EntityManagerFactory, and TransactionManager.


2. Core Concepts

@EnableAutoConfiguration

The @SpringBootApplication meta-annotation contains three things:

@SpringBootConfiguration   // = @Configuration
@EnableAutoConfiguration   // activates auto-config
@ComponentScan             // component scanning
public @interface SpringBootApplication {}

@EnableAutoConfiguration starts the auto-configuration search. In Spring Boot 3.x, the META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports file contains registered auto-configuration classes.

Conditional Annotations

Auto-configuration classes use @Conditional* annotations to control when they activate:

Annotation Condition
@ConditionalOnClass Given class is on the classpath
@ConditionalOnMissingClass Given class is NOT on the classpath
@ConditionalOnBean Given bean exists in the context
@ConditionalOnMissingBean Given bean does NOT exist (user override)
@ConditionalOnProperty Property has a given value
@ConditionalOnWebApplication Web application context
@ConditionalOnResource Given resource is available
@ConditionalOnExpression SpEL expression is true

The "Back Off" Principle

Auto-configuration backs off when a developer defines their own bean. This is implemented with @ConditionalOnMissingBean:

@AutoConfiguration
@ConditionalOnClass(DataSource.class)
public class DataSourceAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean  // If developer defined a DataSource, this doesn't run
    public DataSource dataSource(DataSourceProperties props) {
        return props.initializeDataSourceBuilder().build();
    }
}

spring.factories vs AutoConfiguration.imports

Version Registration mechanism
Spring Boot 2.x META-INF/spring.factories (EnableAutoConfiguration key)
Spring Boot 3.x META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

Spring Boot 3.x still supports spring.factories for backward compatibility, but the new approach is recommended.


3. Practical Usage

Excluding Auto-configuration

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class MyApp { ... }

Or via property:

spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration

Overriding Auto-configuration with Your Own Bean

@Configuration
public class MyDataSourceConfig {
    @Bean
    public DataSource dataSource() {
        HikariDataSource ds = new HikariDataSource();
        ds.setJdbcUrl("jdbc:postgresql://localhost/mydb");
        ds.setUsername("admin");
        return ds;
    }
}
// DataSourceAutoConfiguration backs off due to @ConditionalOnMissingBean

Debug: Which Auto-configs Are Active?

# application.properties
debug=true

This prints the Conditions Evaluation Report at startup:

  • Positive matches: active auto-configuration classes
  • Negative matches: inactive (condition not met)
  • Unconditional classes: configurations without conditions

Actuator Endpoint

management.endpoints.web.exposure.include=conditions

The /actuator/conditions endpoint returns the same information in JSON format at runtime.


4. Code Examples

Simple Auto-configuration Class

@AutoConfiguration
@ConditionalOnClass(ObjectMapper.class)
@EnableConfigurationProperties(JsonProperties.class)
public class JsonAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public ObjectMapper objectMapper(JsonProperties props) {
        ObjectMapper mapper = new ObjectMapper();
        if (props.isIndentOutput()) {
            mapper.enable(SerializationFeature.INDENT_OUTPUT);
        }
        return mapper;
    }
}

ConfigurationProperties for Properties

@ConfigurationProperties(prefix = "app.json")
public class JsonProperties {
    private boolean indentOutput = false;

    public boolean isIndentOutput() { return indentOutput; }
    public void setIndentOutput(boolean v) { this.indentOutput = v; }
}
app.json.indent-output=true

Custom Auto-configuration Module (Library)

my-library/
├── src/main/java/com/example/
│   └── MyLibraryAutoConfiguration.java
└── src/main/resources/META-INF/spring/
    └── org.springframework.boot.autoconfigure.AutoConfiguration.imports

The .imports file contents:

com.example.MyLibraryAutoConfiguration

@AutoConfiguration Ordering

@AutoConfiguration(
    after = DataSourceAutoConfiguration.class,
    before = JpaRepositoriesAutoConfiguration.class
)
public class MyAutoConfiguration { ... }

5. Trade-offs

Aspect Advantage Disadvantage
Automatic configuration Fast development, less boilerplate "Magic" — hard to understand what happens
Convention over configuration Uniform project structure More overrides needed for custom requirements
@ConditionalOnMissingBean User can override anything If you don't know about it, you can't use it
debug=true Full transparency Very verbose output
Custom auto-config module Reusable libraries Extra complexity, harder testing

When NOT Auto-configuration?

  • Very custom configuration: if many overrides are needed, explicit config is cleaner
  • Performance-critical startup: many auto-config classes slow down startup
  • Library development: carefully design the conditional logic

6. Common Mistakes

❌ Auto-config Override Not Working

// BAD — @Bean method in the @SpringBootApplication class
// Component scan may run before auto-config
@SpringBootApplication
public class MyApp {
    @Bean
    public DataSource dataSource() { ... } // May not override!
}

// GOOD — separate @Configuration class
@Configuration
public class MyDataSourceConfig {
    @Bean
    public DataSource dataSource() { ... }
}

❌ Exclude Typo

// BAD — non-existent class, silently ignored
@SpringBootApplication(exclude = {NonExistentAutoConfiguration.class})

// GOOD — excludeName string when class is not on classpath
@SpringBootApplication(excludeName = "com.example.SomeAutoConfiguration")

❌ @ConditionalOnBean Ordering Problem

// BAD — @ConditionalOnBean does not guarantee ordering
// If the bean is created by another auto-config, ordering matters
@AutoConfiguration
public class MyConfig {
    @Bean
    @ConditionalOnBean(DataSource.class) // DataSource may not exist yet!
    public MyRepo repo(DataSource ds) { ... }
}

// GOOD — explicit ordering
@AutoConfiguration(after = DataSourceAutoConfiguration.class)
public class MyConfig { ... }

❌ @ComponentScan in Auto-config Library

// BAD — auto-config library should NOT use @ComponentScan
// Application packages may be scanned too
@AutoConfiguration
@ComponentScan("com.example") // Dangerous!
public class LibraryAutoConfig { ... }

// GOOD — explicit @Bean definitions
@AutoConfiguration
public class LibraryAutoConfig {
    @Bean
    public MyService myService() { return new MyService(); }
}

7. Deep Dive

Auto-configuration Loading Process

  1. SpringApplication.run() starts
  2. @EnableAutoConfigurationAutoConfigurationImportSelector activates
  3. Reads .imports files (and legacy spring.factories)
  4. Collects all auto-configuration classes
  5. Filters by @Conditional* (condition evaluation)
  6. Registers remaining classes as bean definitions
  7. @AutoConfiguration(after/before) determines ordering

@Conditional Internals

Every @Conditional* annotation has a Condition interface implementation behind it:

public interface Condition {
    boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}

ConditionContext provides access to:

  • BeanDefinitionRegistry — existing bean definitions
  • Environment — properties and profiles
  • ResourceLoader — classpath resources
  • ClassLoader — classpath class checking

Auto-configuration vs @Configuration

Aspect @AutoConfiguration @Configuration
Loading From .imports file Component scan / @Import
Ordering after/before attributes No guaranteed order
Purpose Framework/library defaults Application-specific config
@ConditionalOnMissingBean Typical (back-off) Rare

Custom Condition Implementation

public class OnProductionCondition implements Condition {
    @Override
    public boolean matches(ConditionContext ctx, AnnotatedTypeMetadata metadata) {
        String env = ctx.getEnvironment().getProperty("app.env");
        return "production".equals(env);
    }
}

@Configuration
@Conditional(OnProductionCondition.class)
public class ProductionOnlyConfig { ... }

GraalVM Native and Auto-configuration

With Spring Boot 3.x native image support, auto-configuration is evaluated at compile-time (AOT processing). This results in faster startup but runtime @Conditional evaluation is not available.


8. Interview Questions

  1. How does Spring Boot auto-configuration work? @EnableAutoConfiguration activates AutoConfigurationImportSelector, which reads .imports files, collects auto-config classes, evaluates @Conditional* annotations, and registers matching bean definitions.

  2. What is the role of @ConditionalOnMissingBean? If a developer already defined a bean of the same type, auto-configuration backs off. This allows overriding without excluding the entire auto-config class.

  3. How can you find out which auto-configurations are active? debug=true property → Conditions Evaluation Report in the log. Or /actuator/conditions endpoint.

  4. What is the difference between spring.factories and the .imports file? spring.factories is the Spring Boot 2.x approach (key-value pairs). The .imports file is the 3.x standard (one class per line). Both register auto-config classes.

  5. How do you write your own auto-configuration? @AutoConfiguration class + @Conditional* annotations + .imports file under META-INF/spring/. Use @ConditionalOnMissingBean for back-off logic.

  6. Why shouldn't you use @ComponentScan in an auto-config library? Because it could scan application packages too, registering unexpected beans. Use explicit @Bean definitions in auto-config libraries.

  7. What is auto-configuration ordering and why is it important? @AutoConfiguration(after/before) controls loading order. Important when one auto-config bean depends on another (e.g., DataSource needed for JPA auto-config).


9. Glossary

Term Meaning
@EnableAutoConfiguration Annotation that activates auto-config
@AutoConfiguration Marks an auto-configuration class (Boot 3.x)
@ConditionalOnClass Condition: class is on the classpath
@ConditionalOnMissingBean Condition: no such bean → back-off logic
@ConditionalOnProperty Condition: property value
spring.factories Legacy (2.x) auto-config registration file
AutoConfiguration.imports Modern (3.x) auto-config registration file
AutoConfigurationImportSelector Class that reads and filters auto-configs
Conditions Evaluation Report Debug output of active/inactive auto-configs
Back-off Auto-config stepping back when developer defined a bean
Convention over configuration Framework-level defaults, minimal config
AOT processing Compile-time auto-config evaluation (GraalVM native)

10. Cheatsheet

Auto-configuration activation:
  @SpringBootApplication = @Configuration + @EnableAutoConfiguration + @ComponentScan

Registration:
  Boot 3.x:  META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  Boot 2.x:  META-INF/spring.factories (EnableAutoConfiguration key)

Key @Conditional annotations:
  @ConditionalOnClass(Foo.class)          on classpath
  @ConditionalOnMissingBean(Foo.class)    no such bean (back-off)
  @ConditionalOnProperty(name="x")        property exists/value
  @ConditionalOnBean(Foo.class)           bean exists

Back-off pattern:
  @Bean
  @ConditionalOnMissingBean
  public Foo foo() { ... }   // developer can override

Exclusion:
  @SpringBootApplication(exclude = {FooAutoConfiguration.class})
  spring.autoconfigure.exclude=com.example.FooAutoConfiguration

Debug:
  debug=true                              → Conditions Evaluation Report
  /actuator/conditions                    → JSON of running conditions

Ordering:
  @AutoConfiguration(after = A.class, before = B.class)

Common mistakes:
  ✗ @Bean override in @SpringBootApplication class
  ✗ @ConditionalOnBean ordering problem
  ✗ @ComponentScan in auto-config library
  ✗ Using spring.factories in Boot 3.x (deprecated)

🎮 Games

10 questions