Skip to content

Spring Boot · Core

How Spring Boot Auto-Configuration Actually Works

5 min read Updated 2026-06-26 Share:

Practice Auto-Configuration interview questions

Why auto-configuration is the question behind the question

"How does Spring Boot configure all this for me?" is the single most revealing Spring Boot interview topic. A candidate who can explain auto-configuration understands the classpath, conditional beans, bean override semantics, and how to debug a misbehaving context — which is most of what senior Spring work actually requires. This article builds the whole mental model.

The one-sentence definition

Auto-configuration registers beans based on what is on the classpath, what beans already exist, and what properties are set — and it always backs off the moment you provide your own version of something.

@SpringBootApplication // bundles @EnableAutoConfiguration
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}

Add spring-boot-starter-web, and without writing a single @Bean you get an embedded Tomcat, a DispatcherServlet, JSON serialization via Jackson, sensible error handling, and more.

Step 1 — @SpringBootApplication unpacks into three annotations

// @SpringBootApplication ==
@SpringBootConfiguration  // a @Configuration that tests can locate
@EnableAutoConfiguration  // turn the auto-config engine on
@ComponentScan            // discover your own @Component/@Service/@RestController
public class App { }

Component scanning starts in the annotated class's package, which is why the main class belongs in a root package above the rest of your code.

Step 2 — @EnableAutoConfiguration discovers candidates

@EnableAutoConfiguration imports AutoConfigurationImportSelector. That selector does not scan your code. It reads a list of candidate class names from every jar on the classpath:

# Spring Boot 2.7+:
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

# Before 2.7 (the "EnableAutoConfiguration" key in):
META-INF/spring.factories

Each line is one fully-qualified auto-configuration class name. Spring Boot's own spring-boot-autoconfigure jar contributes well over a hundred of them.

Step 3 — @Conditional decides what survives

Loading the candidates is only half the story. Each class is gated by conditions, evaluated during bean-definition registration:

@AutoConfiguration
@ConditionalOnClass(DataSource.class)        // only if JDBC is on the classpath
public class MyDataSourceAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean                 // back off if the app already defines one
    @ConditionalOnProperty(name = "app.db.enabled", havingValue = "true")
    DataSource dataSource() { /* ... */ }
}

The conditions you will see most:

ConditionFires when
@ConditionalOnClass / @ConditionalOnMissingClassA class is / isn't on the classpath
@ConditionalOnBean / @ConditionalOnMissingBeanA bean is / isn't already defined
@ConditionalOnPropertyA property has a given value
@ConditionalOnWebApplicationThe app is servlet / reactive / none
@ConditionalOnResourceA resource file exists

A subtle but important detail: @ConditionalOnClass is evaluated by ASM bytecode inspection, not by loading the class. A missing optional library therefore never throws NoClassDefFoundError — the condition simply evaluates to false.

Step 4 — starters bring the classpath, auto-config reacts

A starter is a dependency aggregator with almost no code of its own:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

It pulls in Spring MVC, Tomcat, Jackson and validation at versions aligned by the Spring Boot BOM. Now that those classes exist on the classpath, the matching @ConditionalOnClass auto-configurations activate. Starters express intent; auto-configuration acts on it.

Overriding: just define your own bean

Because user @Configuration is processed before auto-configuration, and auto-config beans are marked @ConditionalOnMissingBean, overriding is effortless:

@Configuration
public class JacksonConfig {
    @Bean
    ObjectMapper objectMapper() { // Spring Boot's default ObjectMapper now backs off
        return new ObjectMapper()
            .findAndRegisterModules()
            .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    }
}

You rarely need to exclude an auto-configuration just to tweak it. Exclusion is for when you want the whole feature gone:

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

Debugging: read the condition evaluation report

When auto-config does something surprising, don't guess — ask Spring Boot to explain itself:

# application.properties
debug=true

This prints Positive matches (what applied and why) and Negative matches (what backed off and why). The same data is available at runtime from Actuator:

GET /actuator/conditions

A typical negative match reads like: "DataSourceAutoConfiguration did not match: required class found but no 'spring.datasource.url' property." That one line usually ends the debugging session.

Controlling order

When one auto-config depends on another, express the edge — don't try to impose a global order:

@AutoConfiguration(
    after = DataSourceAutoConfiguration.class,    // a DataSource must exist first
    before = TransactionAutoConfiguration.class)  // but configure us before transactions
public class MyOrmAutoConfiguration { }

Use the *Name variants (afterName, beforeName) when the referenced class might be absent at compile time.

Writing your own auto-configuration

To make a library that configures itself in any Spring Boot app:

// 1. The class:
@AutoConfiguration
@ConditionalOnClass(GreetingService.class)
@EnableConfigurationProperties(GreetingProperties.class)
public class GreetingAutoConfiguration {
    @Bean
    @ConditionalOnMissingBean
    GreetingService greetingService(GreetingProperties props) {
        return new GreetingService(props.getPrefix());
    }
}

// 2. Register it in src/main/resources/META-INF/spring/
//    org.springframework.boot.autoconfigure.AutoConfiguration.imports
//    com.example.greeting.GreetingAutoConfiguration

Expose every tunable through a @ConfigurationProperties class so consumers configure behavior via properties, YAML or environment variables — never by editing code. By convention, ship the @AutoConfiguration code in an *-autoconfigure module and a thin *-starter POM that depends on it.

A note on @AutoConfiguration vs @Configuration

@AutoConfiguration is for libraries listed in AutoConfiguration.imports. It implies @Configuration(proxyBeanMethods = false) for faster startup and adds before/after ordering support. In your application you keep writing ordinary @Configuration. With proxyBeanMethods = false you must inject collaborators as @Bean method parameters rather than calling another @Bean method directly — there is no CGLIB proxy to return the singleton.

Recap

Auto-configuration is a four-part story: @SpringBootApplication enables it, an import selector reads candidate classes from AutoConfiguration.imports, @Conditional annotations filter those candidates against the classpath and existing beans, and starters supply the classpath that drives the whole thing. Override by defining your own bean (@ConditionalOnMissingBean does the back-off), debug with debug=true or /actuator/conditions, and package reusable configuration as an @AutoConfiguration class registered in the imports file. Master this and most of Spring Boot stops being magic.

More ways to practice

The self-quiz is live. Join our channel for updates, new content & tech tips.

Join our WhatsApp Channel