Foundation · Understand
How Auto-configuration Works
Boot decides what to configure by evaluating classpath and bean conditions at startup; learn to read those decisions instead of guessing at them.
Primary source: Boot 3.5 auto-configuration
Lesson 5 of 5
1d streakThe Architectural Question
When an expected auto-configured bean behaves unexpectedly or goes missing, guessing costs hours; the Condition Evaluation Report proves which conditions matched, which failed, and which user bean triggered back-off.
What this guide proves
- @EnableAutoConfiguration
- AutoConfiguration.imports
- @ConditionalOnClass
The cost of manual infrastructure
Before Spring Boot, starting a web application required assembling infrastructure by hand. You had to declare a DispatcherServlet, configure content negotiation, register Jackson message converters, and set up an embedded server factory. This boilerplate was identical across projects, but hiding it inside a closed framework prevented teams from customizing the beans they actually needed.
Auto-configuration solves this by turning common infrastructure into conditional configuration. Boot provides the defaults, but evaluates conditions at startup to ensure it never overrides your explicit choices.
Discovery, conditions, and back-off
Auto-configuration is not scanning. When you use @SpringBootApplication, Boot scans your own packages for @Component and @Bean methods. Separately, @EnableAutoConfiguration tells Boot to read META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports from every jar on the classpath. These imports name candidate configuration classes.
- 01Candidate discovery: Boot loads the list of candidate configuration classes from imported framework modules.
- 02Condition evaluation: Boot evaluates @Conditional annotations on each candidate against the current classpath, properties, and existing beans.
- 03Registration: Candidates whose conditions match are registered as BeanDefinitions.
- 04Instantiation: The Spring container creates the actual bean instances from those definitions.
Condition evaluation is evidence-based
Tracing Jackson auto-configuration
Consider JacksonAutoConfiguration in Spring Boot 3.5. It does not apply unconditionally to every application. Its declaration proves it only activates when Jackson is actually on the classpath.
@AutoConfiguration
@ConditionalOnClass(ObjectMapper.class)
public class JacksonAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Jackson2ObjectMapperBuilder.class)
static class JacksonObjectMapperConfiguration {
@Bean
@Primary
@ConditionalOnMissingBean
ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
return builder.createXmlMapper(false).build();
}
}
}The class-level @ConditionalOnClass ensures Boot ignores this configuration entirely if your build does not include Jackson. A positive outer match does not guarantee every nested configuration matches. The nested JacksonObjectMapperConfiguration evaluates its own @ConditionalOnClass(Jackson2ObjectMapperBuilder.class) before deciding to evaluate the @Bean method, which finally checks @ConditionalOnMissingBean to back off if you define your own ObjectMapper. This is the sanctioned override path.
Proving back-off with ApplicationContextRunner
We can prove this behavior programmatically using Boot's ApplicationContextRunner. This utility lets us run a focused context test with specific auto-configurations and our own beans.
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(JacksonAutoConfiguration.class));
@Test
void autoConfigurationBacksOffWhenUserBeanIsPresent() {
this.contextRunner.withUserConfiguration(UserConfiguration.class).run((context) -> {
assertThat(context).hasSingleBean(ObjectMapper.class);
assertThat(context).getBean("myCustomMapper").isSameAs(context.getBean(ObjectMapper.class));
});
}
@Configuration(proxyBeanMethods = false)
static class UserConfiguration {
@Bean
ObjectMapper myCustomMapper() {
return new ObjectMapper();
}
}When we supply UserConfiguration, Boot evaluates JacksonAutoConfiguration, sees our myCustomMapper bean, and skips its own default. The test proves that the container holds exactly one ObjectMapper, and it is the one we defined.
Reading the condition evaluation report
When a bean is missing or unexpected, do not guess. Run your application with the --debug flag to print the Condition Evaluation Report. This trace explains exactly why every candidate matched or failed.
Positive matches:
-----------------
JacksonAutoConfiguration matched:
- @ConditionalOnClass found required class 'com.fasterxml.jackson.databind.ObjectMapper' (OnClassCondition)
JacksonAutoConfiguration.JacksonObjectMapperConfiguration matched:
- @ConditionalOnClass found required class 'org.springframework.http.converter.json.Jackson2ObjectMapperBuilder' (OnClassCondition)
Negative matches:
-----------------
JacksonAutoConfiguration.JacksonObjectMapperConfiguration#jacksonObjectMapper:
- @ConditionalOnMissingBean (types: com.fasterxml.jackson.databind.ObjectMapper; SearchStrategy: all) found beans of type 'com.fasterxml.jackson.databind.ObjectMapper' myCustomMapper (OnBeanCondition)This report confirms the outcome of our experiment: the outer configuration class and its nested configuration matched because Jackson was on the classpath, but the specific bean method backed off because it found myCustomMapper.
Diagnosing missing beans vs. false conditions
A common misconception is assuming that because Tomcat started on port 8080, MVC auto-configuration succeeded and any subsequent 404 must simply be an unmapped path. In reality, Tomcat starting does not even prove that ServletWebServerFactoryAutoConfiguration ran—if an engineer manually declared a custom @Bean public TomcatServletWebServerFactory servletWebServerFactory(), the default auto-configuration completely backed off due to @ConditionalOnMissingBean, yet Tomcat still started! Tomcat starting only proves that a web server factory bean was resolved and bound to a port. A subsequent 404 can stem from three distinct architectural failure modes: MVC auto-configuration did not match, the controller bean was never instantiated, or handler mappings did not register the expected URI.
Use a structured three-step diagnostic tree instead of guessing: (1) Run with --debug and inspect the Condition Evaluation Report to verify that DispatcherServletAutoConfiguration and WebMvcAutoConfiguration matched. (2) Query /actuator/beans to confirm whether your @RestController exists in the ApplicationContext. If absent, do not jump to package-scanning conclusions: an absent bean may be caused by package boundaries outside the @SpringBootApplication scan tree, but equally by @Profile mismatches (e.g. @Profile("dev") running under prod), conditional evaluations (@ConditionalOnProperty, @ConditionalOnClass, @ConditionalOnBean), or @ComponentScan.Filter exclusions. Inspect the Condition Evaluation Report under "Negative matches" to identify the exact condition that backed off. (3) Query /actuator/mappings to verify that DispatcherServlet mapped the exact HTTP verb and path—if the bean exists in the context but returns 404, inspect server.servlet.context-path, path variable regular expressions, trailing slash mismatches, or unmapped HTTP methods.
Active Recall Checkpoint
Your custom controller at com.example.api.OrderController returns 404, but Tomcat started successfully on port 8080 from com.example.app.Application. Does Tomcat starting prove ServletWebServerFactoryAutoConfiguration succeeded?
Show answer & explanation
No. Tomcat starting only proves that a ServletWebServerFactory bean was resolved and initialized—a manually declared ServletWebServerFactory bean causes ServletWebServerFactoryAutoConfiguration to back off via @ConditionalOnMissingBean while Tomcat still starts. Furthermore, an absent controller from /actuator/beans does not uniquely prove scanning failed: it may also be excluded by an active @Profile, a @ConditionalOnProperty condition that evaluated false, or custom ComponentScan filter rules. Always check the Condition Evaluation Report (--debug) to isolate the exact cause.
How to change framework defaults
Use it when
Rely on application properties (e.g., server.port) when the auto-configured bean is acceptable but needs minor tuning.
Reconsider when
Define your own @Bean to trigger back-off when properties cannot express your requirement. Use exclusions only when replacing the entire capability.
Runtime Architecture & Flow
High-value visual model
Auto-configuration Decision Tree
Auto-configuration is conditional Java configuration. The important question is which condition matched, backed off, or lost to your explicit bean.
1. Is the library on the classpath?
@ConditionalOnClass checks whether the feature is available.
2. Is the feature enabled by config?
@ConditionalOnProperty lets external configuration turn infrastructure on or off.
3. Did the user define a bean?
@ConditionalOnMissingBean backs off when application code takes control.
4. Register default infrastructure
Boot contributes the default bean only after the conditions pass.
Working Implementation
Code Walkthrough
Inspect the Condition Evaluation Report with `--debug` or ApplicationContextRunner to verify JacksonAutoConfiguration matches, then declare a custom ObjectMapper bean and observe @ConditionalOnMissingBean back-off.
package com.example.autoconfig;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
class JacksonAutoConfigurationTest {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(JacksonAutoConfiguration.class));
@Test
void baselineSuppliesDefaultObjectMapper() {
this.contextRunner.run((context) -> {
assertThat(context).hasSingleBean(ObjectMapper.class);
assertThat(context).hasBean("jacksonObjectMapper");
});
}
@Test
void autoConfigurationBacksOffWhenUserBeanIsPresent() {
this.contextRunner.withUserConfiguration(UserConfiguration.class).run((context) -> {
assertThat(context).hasSingleBean(ObjectMapper.class);
assertThat(context).getBean("myCustomMapper")
.isSameAs(context.getBean(ObjectMapper.class));
assertThat(context).doesNotHaveBean("jacksonObjectMapper");
});
}
@Test
void negativeControlWithoutAutoConfigurationYieldsNoMapper() {
new ApplicationContextRunner().run((context) -> {
assertThat(context).doesNotHaveBean(ObjectMapper.class);
});
}
@Test
void conditionalOnClassDisablesCandidateWhenLibraryIsAbsent() {
this.contextRunner
.withClassLoader(new FilteredClassLoader(ObjectMapper.class))
.run((context) -> {
assertThat(context).doesNotHaveBean(ObjectMapper.class);
});
}
@Configuration(proxyBeanMethods = false)
static class UserConfiguration {
@Bean
ObjectMapper myCustomMapper() {
return new ObjectMapper();
}
}
}Synthesis & Production Verification
Production Acceptance Criteria
- Read positive and negative matches in the Condition Evaluation Report
- Demonstrate @ConditionalOnClass matching when Jackson is present on the classpath
- Verify user-declared @Bean triggers @ConditionalOnMissingBean back-off using ApplicationContextRunner or live debug output
Key Takeaway
Auto-configuration is inspectable conditional configuration: classpath, application type, properties, and existing beans determine which defaults enter the context.
Architectural & Technical Review
Core Interview Questions
Q01How does Spring Boot decide whether an auto-configuration applies?Show answer
Boot discovers candidate configuration classes and evaluates their conditions against runtime evidence such as available classes, properties, application type, resources, and existing beans.
Q02What does @ConditionalOnMissingBean achieve?Show answer
It creates a default only when the application has not already supplied the guarded capability, which gives application configuration precedence without disabling all auto-configuration.
Q03How should you diagnose an expected auto-configured bean that is absent?Show answer
Read the candidate in the condition evaluation report and follow its failed condition to the missing class, property, application type, resource, or existing-bean state.
Production Pitfalls
Critical Anti-Patterns
- Pitfall: Assuming a starter guarantees every bean — The starter makes classes available, but every candidate still evaluates its own conditions.
- Pitfall: Using a broad exclusion for one unwanted default — Prefer a documented property or narrow user bean unless the application intends to own the entire capability.
Module Certification Quiz
Prove Module Mastery
Comprehensive question set covering all lessons in this module.
Primary Documentation & Specifications
Lesson Primary References
Supplementary Module Resources