CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-springframework-boot--spring-boot-autoconfigure

Spring Boot AutoConfigure provides auto-configuration capabilities that automatically configure Spring applications based on jar dependencies present on the classpath

Pending
Overview
Eval results
Files

common-patterns.mddocs/examples/

Common Patterns

Real-world patterns and examples for Spring Boot AutoConfigure.

Pattern 1: Database Configuration

Scenario

Auto-configure database based on available drivers.

Implementation

@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();
    }
}

Usage

# Production
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=user
spring.datasource.password=secret

# Development (uses embedded H2)
# No configuration needed

Pattern 2: Feature Flags

Scenario

Enable/disable features via configuration.

Implementation

@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();
    }
}

Usage

features.caching=enabled
features.metrics=enabled

Pattern 3: Environment-Specific Beans

Scenario

Different implementations for dev/test/prod.

Implementation

@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();
    }
}

Usage

# Development
java -jar app.jar --spring.profiles.active=dev

# Production
java -jar app.jar --spring.profiles.active=prod

Pattern 4: Progressive Fallback

Scenario

Try multiple cache implementations, fall back to simple.

Implementation

@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();
    }
}

Pattern 5: Conditional Security

Scenario

Apply security only when needed.

Implementation

@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();
    }
}

Usage

# Enable security (default)
security.enabled=true

# Disable for development
security.enabled=false

Pattern 6: Multi-Tenant Configuration

Scenario

Configure multiple data sources for tenants.

Implementation

@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
    }
}

Usage

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: pass1

Pattern 7: Conditional REST Client

Scenario

Auto-configure REST client when service is available.

Implementation

@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);
    }
}

Usage

api.client.url=https://api.example.com
api.client.token=${API_TOKEN}

Pattern 8: Async Task Execution

Scenario

Configure async execution based on environment.

Implementation

@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();
    }
}

Usage

# Use thread pool (Java 17+)
async.executor.type=thread-pool

# Use virtual threads (Java 21+)
async.executor.type=virtual

Pattern 9: Health Indicators

Scenario

Auto-configure health checks for services.

Implementation

@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();
            }
        };
    }
}

Pattern 10: Conditional Logging

Scenario

Configure logging based on environment.

Implementation

@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);
    }
}

Best Practices Summary

  1. Use Sensible Defaults: Enable features by default, allow opt-out
  2. Fail Fast: Validate configuration early
  3. Allow Overrides: Use @ConditionalOnMissingBean
  4. Order Conditions: Fast conditions first
  5. Document Behavior: Clear property documentation
  6. Test Thoroughly: Test all conditional paths

Next Steps

  • Real-World Scenarios - Production examples
  • Edge Cases - Handle special situations
  • Custom Auto-Configurations - Build your own

See Also

Install with Tessl CLI

npx tessl i tessl/maven-org-springframework-boot--spring-boot-autoconfigure

docs

examples

common-patterns.md

edge-cases.md

real-world-scenarios.md

index.md

tile.json