Spring Boot AutoConfigure provides auto-configuration capabilities that automatically configure Spring applications based on jar dependencies present on the classpath
—
Real-world patterns and examples for Spring Boot AutoConfigure.
Auto-configure database based on available drivers.
@AutoConfiguration
@ConditionalOnClass(DataSource.class)
public class DatabaseAutoConfiguration {
@Bean
@ConditionalOnClass(name = "com.mysql.cj.jdbc.Driver")
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "spring.datasource", name = "url")
public DataSource mysqlDataSource(DataSourceProperties properties) {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl(properties.getUrl());
dataSource.setUsername(properties.getUsername());
dataSource.setPassword(properties.getPassword());
return dataSource;
}
@Bean
@ConditionalOnMissingBean(DataSource.class)
public DataSource embeddedDataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.build();
}
}# Production
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=user
spring.datasource.password=secret
# Development (uses embedded H2)
# No configuration neededEnable/disable features via configuration.
@Configuration
public class FeatureFlagConfiguration {
@Bean
@ConditionalOnProperty(
prefix = "features",
name = "caching",
havingValue = "enabled"
)
public CacheManager cacheManager() {
return new CaffeineCacheManager();
}
@Bean
@ConditionalOnProperty(
prefix = "features",
name = "metrics",
havingValue = "enabled",
matchIfMissing = true // Enabled by default
)
public MetricsCollector metricsCollector() {
return new MetricsCollector();
}
}features.caching=enabled
features.metrics=enabledDifferent implementations for dev/test/prod.
@Configuration
public class EnvironmentConfiguration {
@Bean
@Profile("dev")
public EmailService devEmailService() {
return new MockEmailService(); // Logs emails
}
@Bean
@Profile({"test", "prod"})
public EmailService realEmailService() {
return new SmtpEmailService(); // Sends real emails
}
@Bean
@Profile("prod")
@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)
public ServiceDiscovery k8sServiceDiscovery() {
return new KubernetesServiceDiscovery();
}
}# Development
java -jar app.jar --spring.profiles.active=dev
# Production
java -jar app.jar --spring.profiles.active=prodTry multiple cache implementations, fall back to simple.
@AutoConfiguration
public class CacheAutoConfiguration {
@Bean
@ConditionalOnClass(name = "redis.clients.jedis.Jedis")
@ConditionalOnBean(RedisConnectionFactory.class)
@ConditionalOnMissingBean(CacheManager.class)
public CacheManager redisCacheManager(RedisConnectionFactory factory) {
return RedisCacheManager.builder(factory).build();
}
@Bean
@ConditionalOnClass(name = "com.github.benmanes.caffeine.cache.Caffeine")
@ConditionalOnMissingBean(CacheManager.class)
public CacheManager caffeineCacheManager() {
return new CaffeineCacheManager();
}
@Bean
@ConditionalOnMissingBean
public CacheManager simpleCacheManager() {
return new ConcurrentMapCacheManager();
}
}Apply security only when needed.
@Configuration
@ConditionalOnClass(SecurityFilterChain.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
public class SecurityAutoConfiguration {
@Bean
@ConditionalOnProperty(
name = "security.enabled",
havingValue = "true",
matchIfMissing = true
)
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults());
return http.build();
}
@Bean
@ConditionalOnProperty(name = "security.enabled", havingValue = "false")
public SecurityFilterChain disabledSecurity(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(auth -> auth.anyRequest().permitAll());
return http.build();
}
}# Enable security (default)
security.enabled=true
# Disable for development
security.enabled=falseConfigure multiple data sources for tenants.
@Configuration
@EnableConfigurationProperties(TenantProperties.class)
public class MultiTenantConfiguration {
@Bean
public DataSource dataSource(TenantProperties properties) {
Map<Object, Object> targetDataSources = new HashMap<>();
properties.getTenants().forEach((name, config) -> {
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl(config.getUrl());
ds.setUsername(config.getUsername());
ds.setPassword(config.getPassword());
targetDataSources.put(name, ds);
});
AbstractRoutingDataSource routingDataSource =
new TenantRoutingDataSource();
routingDataSource.setTargetDataSources(targetDataSources);
routingDataSource.setDefaultTargetDataSource(
targetDataSources.get("default")
);
return routingDataSource;
}
}
@ConfigurationProperties(prefix = "tenants")
public class TenantProperties {
private Map<String, TenantConfig> tenants = new HashMap<>();
public static class TenantConfig {
private String url;
private String username;
private String password;
// Getters and setters
}
}tenants:
tenants:
default:
url: jdbc:mysql://localhost:3306/tenant_default
username: user
password: pass
tenant1:
url: jdbc:mysql://localhost:3306/tenant_1
username: user1
password: pass1Auto-configure REST client when service is available.
@AutoConfiguration
@ConditionalOnClass(RestClient.class)
@EnableConfigurationProperties(ApiClientProperties.class)
public class ApiClientAutoConfiguration {
@Bean
@ConditionalOnProperty(prefix = "api.client", name = "url")
@ConditionalOnMissingBean
public RestClient apiRestClient(ApiClientProperties properties) {
return RestClient.builder()
.baseUrl(properties.getUrl())
.defaultHeader("Authorization", "Bearer " + properties.getToken())
.build();
}
@Bean
@ConditionalOnBean(RestClient.class)
public ApiService apiService(RestClient restClient) {
return new ApiService(restClient);
}
}api.client.url=https://api.example.com
api.client.token=${API_TOKEN}Configure async execution based on environment.
@Configuration
@EnableAsync
public class AsyncConfiguration {
@Bean
@Profile("prod")
@ConditionalOnProperty(name = "async.executor.type", havingValue = "thread-pool")
public Executor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(50);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("async-");
executor.initialize();
return executor;
}
@Bean
@ConditionalOnJava(value = JavaVersion.TWENTY_ONE, range = Range.EQUAL_OR_NEWER)
@ConditionalOnProperty(name = "async.executor.type", havingValue = "virtual")
public Executor virtualThreadExecutor() {
return Executors.newVirtualThreadPerTaskExecutor();
}
@Bean
@ConditionalOnMissingBean(Executor.class)
public Executor simpleExecutor() {
return Executors.newCachedThreadPool();
}
}# Use thread pool (Java 17+)
async.executor.type=thread-pool
# Use virtual threads (Java 21+)
async.executor.type=virtualAuto-configure health checks for services.
@AutoConfiguration
@ConditionalOnClass(HealthIndicator.class)
public class HealthIndicatorAutoConfiguration {
@Bean
@ConditionalOnBean(DataSource.class)
@ConditionalOnMissingBean(name = "dbHealthIndicator")
public HealthIndicator dbHealthIndicator(DataSource dataSource) {
return new DataSourceHealthIndicator(dataSource);
}
@Bean
@ConditionalOnBean(RedisConnectionFactory.class)
@ConditionalOnMissingBean(name = "redisHealthIndicator")
public HealthIndicator redisHealthIndicator(
RedisConnectionFactory factory) {
return new RedisHealthIndicator(factory);
}
@Bean
@ConditionalOnProperty(prefix = "api.client", name = "url")
public HealthIndicator apiHealthIndicator(RestClient restClient) {
return () -> {
try {
restClient.get().uri("/health").retrieve().toBodilessEntity();
return Health.up().build();
} catch (Exception e) {
return Health.down(e).build();
}
};
}
}Configure logging based on environment.
@Configuration
public class LoggingConfiguration {
@Bean
@Profile("dev")
public LoggingFilter verboseLoggingFilter() {
return new LoggingFilter(LogLevel.DEBUG);
}
@Bean
@Profile("prod")
@ConditionalOnProperty(name = "logging.requests.enabled", havingValue = "true")
public LoggingFilter productionLoggingFilter() {
return new LoggingFilter(LogLevel.INFO);
}
}@ConditionalOnMissingBean