Spring Boot
Spring Boot Auto Configuration
Auto-configuration is the engine behind Spring Boot's 'just works' experience. It examines your classpath, existing beans, and properties, then automatically configures hundreds of Spring beans without a single line of configuration from you. Understanding how it works — and how to override, extend, and debug it — turns Spring Boot from magic into a tool you fully control.
What Auto-Configuration Does
When you start a Spring Boot application, before any of your beans are created, Spring Boot runs through a list of ~150 auto-configuration classes. Each one is a @Configuration class that conditionally creates beans based on what's available in the environment.
The key word is conditionally. Auto-configuration never fights with your code. Every auto-configured bean carries a @ConditionalOnMissingBean annotation — if you've already defined a bean of that type, the auto-configuration skips it entirely. This is the "opinionated but not dictatorial" design: Spring Boot provides sensible defaults that step aside the moment you make an explicit choice.
The result: add spring-boot-starter-data-jpa to your dependencies, set three properties, and you have a fully configured JPA stack — DataSource, connection pool, EntityManagerFactory, TransactionManager, and Spring Data repositories — without writing a single @Bean method.
How Auto-Configuration Is Triggered
Auto-configuration starts with @EnableAutoConfiguration — one of the three annotations inside @SpringBootApplication. It imports AutoConfigurationImportSelector, which reads a file listing every auto-configuration class available on the classpath.
Java
// @SpringBootApplication expands to:
@Configuration
@EnableAutoConfiguration // triggers auto-configuration
@ComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
// @EnableAutoConfiguration imports AutoConfigurationImportSelector
// which reads this file from every JAR on the classpath:
// META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
// Example entries in that file (from spring-boot-autoconfigure.jar):
// org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
// org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration
// org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
// org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
// org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration
// org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration
// ... ~150 more
// Each listed class is a @Configuration class with @Conditional checks.
// Spring Boot loads ALL of them, but only APPLIES those whose
// conditions are satisfied by the current environment.The @Conditional Annotations
Each auto-configuration class uses @Conditional annotations to decide whether it should apply. These are the precise conditions Spring Boot evaluates before creating any auto-configured bean.
Java
// @ConditionalOnClass — applies ONLY if a class is present on the classpath:
@ConditionalOnClass(DataSource.class)
// @ConditionalOnMissingClass — applies ONLY if a class is NOT on the classpath:
@ConditionalOnMissingClass("com.example.CustomDataSource")
// @ConditionalOnBean — applies ONLY if a specific bean already exists:
@ConditionalOnBean(DataSource.class)
// @ConditionalOnMissingBean — applies ONLY if a bean does NOT exist yet:
// This is what allows YOUR bean definition to override auto-configuration
@ConditionalOnMissingBean(DataSource.class)
// @ConditionalOnProperty — applies based on a property value:
@ConditionalOnProperty(
prefix = "spring.datasource",
name = "url"
)
// Also supports havingValue and matchIfMissing:
@ConditionalOnProperty(
name = "app.cache.enabled",
havingValue = "true",
matchIfMissing = false
)
// @ConditionalOnWebApplication — applies in servlet web apps only:
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
// @ConditionalOnNotWebApplication — applies in non-web apps only:
@ConditionalOnNotWebApplication
// @ConditionalOnExpression — applies based on a SpEL expression:
@ConditionalOnExpression("${app.feature.enabled:false} && '${app.env}' != 'test'")
// @ConditionalOnSingleCandidate — applies if exactly one bean of the type exists:
@ConditionalOnSingleCandidate(DataSource.class)
// @ConditionalOnResource — applies if a resource file exists on the classpath:
@ConditionalOnResource(resources = "classpath:myconfig.xml")A Real Auto-Configuration Class — Dissected
Here's what a real Spring Boot auto-configuration class looks like internally. This is a simplified version of DataSourceAutoConfiguration — the class that automatically creates your database connection pool.
Java
// Simplified DataSourceAutoConfiguration (from spring-boot-autoconfigure):
@AutoConfiguration(before = SqlInitializationAutoConfiguration.class)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {
// Inner class for embedded databases (H2, HSQL, Derby):
@Configuration(proxyBeanMethods = false)
@Conditional(EmbeddedDatabaseCondition.class)
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
@Import(EmbeddedDataSourceConfiguration.class)
protected static class EmbeddedDatabaseConfiguration { }
// Inner class for pooled databases (MySQL, PostgreSQL, etc.):
@Configuration(proxyBeanMethods = false)
@Conditional(PooledDataSourceCondition.class)
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
protected static class PooledDataSourceConfiguration {
@Bean
@ConditionalOnMissingBean(JdbcConnectionDetails.class)
PropertiesJdbcConnectionDetails jdbcConnectionDetails(DataSourceProperties properties) {
return new PropertiesJdbcConnectionDetails(properties);
}
}
}
// DataSourceConfiguration.Hikari — how HikariCP is auto-configured:
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(HikariDataSource.class) // only if HikariCP is on classpath
@ConditionalOnMissingBean(DataSource.class) // only if no DataSource defined yet
@ConditionalOnProperty(
name = "spring.datasource.type",
havingValue = "com.zaxxer.hikari.HikariDataSource",
matchIfMissing = true // default when type not set
)
static class Hikari {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.hikari")
HikariDataSource dataSource(JdbcConnectionDetails connectionDetails) {
HikariDataSource ds = createDataSource(connectionDetails, HikariDataSource.class);
if (StringUtils.hasLength(connectionDetails.getDriverClassName())) {
ds.setDriverClassName(connectionDetails.getDriverClassName());
}
return ds;
}
}
// Every spring.datasource.hikari.* property maps to a HikariDataSource setterAuto-Configuration Ordering
Auto-configuration classes can declare ordering relationships — some must run before others to ensure dependencies between auto-configured beans are satisfied correctly.
Java
// @AutoConfiguration has before and after attributes for ordering:
@AutoConfiguration(before = WebMvcAutoConfiguration.class)
public class MyWebFilterAutoConfiguration { }
// Ensures this auto-config runs BEFORE WebMvcAutoConfiguration
@AutoConfiguration(after = DataSourceAutoConfiguration.class)
public class JpaRepositoriesAutoConfiguration { }
// JPA repos need the DataSource to be configured first
// @AutoConfigureBefore and @AutoConfigureAfter (older style — still valid):
@AutoConfigureBefore(SecurityAutoConfiguration.class)
@AutoConfigureAfter({ DataSourceAutoConfiguration.class,
JdbcTemplateAutoConfiguration.class })
public class MySecurityAutoConfiguration { }
// @AutoConfigureOrder for numeric ordering:
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
public class EarlyAutoConfiguration { }
// The full ordering chain for a JPA web app (simplified):
// 1. PropertyPlaceholderAutoConfiguration — load properties
// 2. DataSourceAutoConfiguration — create DataSource
// 3. DataSourceTransactionManagerAutoConfiguration — transactions
// 4. HibernateJpaAutoConfiguration — EntityManagerFactory
// 5. JpaRepositoriesAutoConfiguration — Spring Data repos
// 6. WebMvcAutoConfiguration — DispatcherServlet, MVC config
// 7. SecurityAutoConfiguration — Spring Security filter chain
// 8. Your @Configuration classes — your beans, using all of the aboveOverriding Auto-Configuration
Overriding auto-configuration is the most common customization pattern. Because every auto-configured bean uses @ConditionalOnMissingBean, defining your own bean of the same type completely replaces the auto-configured one.
Java
// Example 1 — Override the auto-configured DataSource:
@Configuration
public class DataSourceConfig {
@Bean
@Primary
public DataSource dataSource() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
config.setUsername("root");
config.setPassword("secret");
config.setMaximumPoolSize(20);
config.setMinimumIdle(5);
config.setConnectionTimeout(30_000);
config.addDataSourceProperty("cachePrepStmts", "true");
config.addDataSourceProperty("prepStmtCacheSize", "250");
return new HikariDataSource(config);
}
}
// Spring Boot's DataSourceAutoConfiguration sees your DataSource bean
// and @ConditionalOnMissingBean causes it to skip entirely
// Example 2 — Override the auto-configured Jackson ObjectMapper:
@Configuration
public class JacksonConfig {
@Bean
@Primary
public ObjectMapper objectMapper() {
return JsonMapper.builder()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, true)
.addModule(new JavaTimeModule())
.addModule(new Jdk8Module())
.build();
}
}
// Example 3 — Extend Spring MVC auto-configuration without replacing it:
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("https://myapp.com")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowCredentials(true);
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new RequestLoggingInterceptor());
}
}
// WebMvcAutoConfiguration detects WebMvcConfigurer and applies your additions
// while keeping all its own defaults — no need to replace the entire auto-configExcluding Auto-Configuration
Sometimes you want to completely prevent a specific auto-configuration from running — disable security in a test, prevent DataSource creation in a module that has no database, or remove an unwanted default behavior entirely.
Java
// Method 1 — Exclude in @SpringBootApplication:
@SpringBootApplication(exclude = {
SecurityAutoConfiguration.class,
UserDetailsServiceAutoConfiguration.class
})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
// Method 2 — Exclude in @EnableAutoConfiguration:
@Configuration
@EnableAutoConfiguration(exclude = {
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
HibernateJpaAutoConfiguration.class
})
public class NoDbConfig { }
// Method 3 — Exclude via application.properties (no recompile needed):
// spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
// Method 4 — Exclude in a specific test:
@SpringBootTest
@EnableAutoConfiguration(exclude = SecurityAutoConfiguration.class)
class ServiceTest { }
// Method 5 — Exclude via test profile:
// src/test/resources/application-test.properties:
// spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
@SpringBootTest
@ActiveProfiles("test")
class AnotherServiceTest { }Debugging Auto-Configuration
When auto-configuration behaves unexpectedly — a bean isn't being created, a configuration isn't taking effect, or something is configured you didn't expect — Spring Boot provides detailed diagnostic output.
Java
// Enable the auto-configuration conditions report:
// Option 1 — application.properties:
// debug=true
// Option 2 — command line flag:
// java -jar app.jar --debug
// Option 3 — environment variable:
// DEBUG=true java -jar app.jar
// The report prints three sections on startup:
// POSITIVE MATCHES (auto-configs that WERE applied):
// DataSourceAutoConfiguration matched:
// - @ConditionalOnClass found required class 'javax.sql.DataSource'
// - @ConditionalOnMissingBean did not find any DataSource beans
// NEGATIVE MATCHES (auto-configs that were SKIPPED):
// MongoAutoConfiguration did not match:
// - @ConditionalOnClass did not find required class 'com.mongodb.client.MongoClient'
//
// RabbitAutoConfiguration did not match:
// - @ConditionalOnClass did not find required class 'com.rabbitmq.client.Channel'
// EXCLUSIONS:
// SecurityAutoConfiguration — excluded by user
// Programmatic inspection — list every bean in the context:
@Component
public class AutoConfigInspector implements CommandLineRunner {
@Autowired
private ApplicationContext context;
@Override
public void run(String... args) {
String[] beans = context.getBeanDefinitionNames();
Arrays.sort(beans);
for (String bean : beans) {
System.out.println(bean);
}
System.out.println("Total beans: " + beans.length);
}
}
// Actuator endpoints (add spring-boot-starter-actuator):
// GET /actuator/beans — all beans with types and dependencies
// GET /actuator/conditions — full conditions report (same as debug=true)
// GET /actuator/env — all properties and which source they came fromWriting Your Own Auto-Configuration
You can write auto-configuration classes for your own libraries or shared internal modules. This is the same mechanism Spring Boot uses internally — your auto-configuration works identically to any built-in one.
Java
// Step 1 — Define a properties class:
@ConfigurationProperties(prefix = "app.audit")
public class AuditProperties {
private boolean enabled = true;
private int retentionDays = 90;
private String tableName = "audit_log";
// getters and setters
}
// Step 2 — Write the auto-configuration class:
@AutoConfiguration
@ConditionalOnClass(JdbcTemplate.class)
@ConditionalOnProperty(
prefix = "app.audit",
name = "enabled",
havingValue = "true",
matchIfMissing = true
)
@EnableConfigurationProperties(AuditProperties.class)
public class AuditAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public AuditService auditService(JdbcTemplate jdbcTemplate,
AuditProperties properties) {
return new JdbcAuditService(
jdbcTemplate,
properties.getTableName(),
properties.getRetentionDays()
);
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(AuditService.class)
public AuditAspect auditAspect(AuditService auditService) {
return new AuditAspect(auditService);
}
}
// Step 3 — Register the auto-configuration class:
// Create file: src/main/resources/META-INF/spring/
// org.springframework.boot.autoconfigure.AutoConfiguration.imports
// Add one line: com.example.audit.AuditAutoConfiguration
// Step 4 — Test it applies correctly:
@SpringBootTest
class AuditAutoConfigurationTest {
@Autowired(required = false)
private AuditService auditService;
@Test
void auditServiceIsAutoConfigured() {
assertThat(auditService).isNotNull();
}
}
// Step 5 — Test that a user-defined bean overrides auto-config:
@SpringBootTest
class AuditAutoConfigOverrideTest {
@TestConfiguration
static class CustomAuditConfig {
@Bean
public AuditService auditService() {
return new NoOpAuditService();
}
}
@Autowired
private AuditService auditService;
@Test
void customBeanOverridesAutoConfig() {
assertThat(auditService).isInstanceOf(NoOpAuditService.class);
}
}Adding the Configuration Processor for IDE Support
Adding spring-boot-configuration-processor to your project generates metadata that enables IDE auto-completion for your custom @ConfigurationProperties classes — the same hints you get when typing spring.datasource.* in application.properties.
Java
<!-- Add to pom.xml — generates META-INF/spring-configuration-metadata.json at compile time: -->
<!--
dependency:
groupId: org.springframework.boot
artifactId: spring-boot-configuration-processor
optional: true
The optional=true flag ensures it is excluded from the final JAR.
After adding this, rebuild the project.
Your IDE will then show hints for app.audit.retention-days,
app.audit.table-name, app.audit.enabled, and so on.
-->
// Gradle equivalent (add to build.gradle):
// annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
// After adding the processor, properties show hints in application.properties:
// app.audit.enabled=true
// app.audit.retention-days=30
// app.audit.table-name=my_audit_log@ConditionalOnProperty — In Depth
@ConditionalOnProperty is the most frequently used conditional in application-level configuration. It enables feature-flag style behavior where functionality can be toggled on and off via properties without changing code.
Java
// Basic — bean created only when property equals "true":
@Configuration
@ConditionalOnProperty(name = "app.feature.metrics", havingValue = "true")
public class MetricsConfiguration {
@Bean
public MetricsCollector metricsCollector() {
return new PrometheusMetricsCollector();
}
}
// app.feature.metrics=true → bean created
// app.feature.metrics=false → bean NOT created
// property absent → bean NOT created (matchIfMissing defaults to false)
// matchIfMissing=true — opt-out behavior (enabled unless explicitly disabled):
@Configuration
@ConditionalOnProperty(
name = "app.feature.caching",
havingValue = "true",
matchIfMissing = true
)
public class CachingConfiguration { }
// property absent → bean CREATED
// app.feature.caching=true → bean CREATED
// app.feature.caching=false → bean NOT created
// Using prefix to separate namespace from property name:
@ConditionalOnProperty(prefix = "app.mail", name = "enabled")
// checks property: app.mail.enabled
// Multiple properties — ALL must match:
@ConditionalOnProperty(
name = { "app.feature.analytics", "app.feature.tracking" },
havingValue = "true"
)
public class FullAnalyticsConfig { }
// Combined with @Profile:
@Configuration
@Profile("prod")
@ConditionalOnProperty(name = "app.payments.provider", havingValue = "stripe")
public class StripePaymentConfig {
@Bean
public PaymentGateway stripeGateway(@Value("${stripe.api.key}") String apiKey) {
return new StripePaymentGateway(apiKey);
}
}
// Only active in prod profile AND when app.payments.provider=stripe
// Feature flag pattern — toggle entire feature sets:
@Configuration
@ConditionalOnProperty(name = "app.notifications.enabled", havingValue = "true", matchIfMissing = true)
public class NotificationConfig {
@Bean
public EmailNotificationService emailNotificationService() {
return new EmailNotificationService();
}
@Bean
public SmsNotificationService smsNotificationService() {
return new SmsNotificationService();
}
}