docs
Core library providing essential Spring Boot functionality for bootstrapping, configuration, logging, and application lifecycle management.
npx @tessl/cli install tessl/maven-org-springframework-boot--spring-boot@4.0.0The core library providing essential Spring Boot functionality for bootstrapping, configuration, logging, and application lifecycle management.
Package Name: spring-boot
Group ID: org.springframework.boot
Package Type: maven
Language: Java
Installation:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<version>4.0.0</version>
</dependency>Gradle:
implementation 'org.springframework.boot:spring-boot:4.0.0'import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;Common imports for configuration:
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;Common imports for lifecycle:
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.context.event.*;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
@Bean
public ApplicationRunner runner() {
return args -> {
System.out.println("Application started!");
};
}
}
@ConfigurationProperties(prefix = "app")
class AppConfig {
private String name;
private int timeout = 30;
// Getters and setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getTimeout() { return timeout; }
public void setTimeout(int timeout) { this.timeout = timeout; }
}This documentation has been enhanced for AI coding agents with comprehensive examples, complete API signatures, thread safety notes, error handling patterns, and production-ready usage patterns.
Package: org.springframework.boot
Maven: org.springframework.boot:spring-boot:4.0.0
Module: Core library providing essential Spring Boot functionality
| Task | Primary Classes | Key Method | Doc Link |
|---|---|---|---|
| Bootstrap application | SpringApplication | run(Class, String[]) | bootstrapping.md |
| Type-safe config | @ConfigurationProperties | Enable with @EnableConfigurationProperties | configuration-properties.md |
| Lifecycle hooks | ApplicationRunner | run(ApplicationArguments) | lifecycle-events.md |
| Health state | ApplicationAvailability | getLivenessState(), getReadinessState() | availability.md |
| Configure logging | LoggingSystem | setLogLevel(String, LogLevel) | logging.md |
| SSL/TLS setup | SslBundles | getBundle(String) | ssl-tls.md |
| Parse JSON | JsonParser | parseMap(String), parseList(String) | json-support.md |
| Failure analysis | FailureAnalyzer | analyze(Throwable, Throwable) | diagnostics.md |
| Task execution | ThreadPoolTaskExecutorBuilder | build() | task-execution.md |
| Register servlet | ServletRegistrationBean | Constructor + setUrlMappings() | web-support.md |
| Package Pattern | Documentation File |
|---|---|
org.springframework.boot.SpringApplication* | bootstrapping.md |
org.springframework.boot.bootstrap.* | bootstrap.md |
org.springframework.boot.builder.* | builder.md |
org.springframework.boot.context.annotation.* | configuration-annotations.md |
org.springframework.boot.context.properties.* | configuration-properties.md |
org.springframework.boot.context.config.* | configuration-data.md |
org.springframework.boot.context.event.* | lifecycle-events.md |
org.springframework.boot.availability.* | availability.md |
org.springframework.boot.logging.* | logging.md |
org.springframework.boot.ssl.* | ssl-tls.md |
org.springframework.boot.json.* | json-support.md |
org.springframework.boot.diagnostics.* | diagnostics.md |
org.springframework.boot.web.* | web-support.md |
org.springframework.boot.task.* | task-execution.md |
org.springframework.boot.system.* | system-utilities.md |
org.springframework.boot.convert.* | conversion.md |
org.springframework.boot.env.* | environment-property-sources.md |
org.springframework.boot.info.* | application-info.md |
org.springframework.boot.cloud.* | cloud-platform.md |
org.springframework.boot.admin.* | admin-jmx.md |
org.springframework.boot.ansi.* | ansi-support.md |
org.springframework.boot.origin.* | origin-tracking.md |
org.springframework.boot.*Aot* | aot-native-image.md |
org.springframework.boot.support.* | support.md |
org.springframework.boot.validation.* | validation.md |
org.springframework.boot.thread.* | threading.md |
org.springframework.boot.util.* | utilities.md |
org.springframework.boot.io.* | resource-loading.md |
| Criterion | ApplicationRunner | CommandLineRunner |
|---|---|---|
| Parsed arguments needed | ✓ Use this | ✗ |
| Raw String[] needed | ✗ | ✓ Use this |
| Access named options (--opt=val) | ✓ Use this | ✗ |
| Simple arg processing | Either (prefer ApplicationRunner) | ✓ |
Example:
// ApplicationRunner - parsed arguments
@Component
class MyRunner implements ApplicationRunner {
public void run(ApplicationArguments args) {
if (args.containsOption("debug")) { /* ... */ }
}
}
// CommandLineRunner - raw arguments
@Component
class MyRunner implements CommandLineRunner {
public void run(String... args) {
// Process raw args
}
}This tile documents the Spring Boot Core library (org.springframework.boot:spring-boot@4.0.0), which provides the foundational infrastructure for bootstrapping, configuring, and running Spring Boot applications. It is the essential dependency for all Spring Boot applications.
org.springframework.boot
├── admin # JMX administration support
├── availability # Application availability states (liveness/readiness)
├── cloud # Cloud platform detection
├── context
│ ├── config # Configuration data loading
│ ├── event # Application lifecycle events
│ ├── metrics # Startup metrics and buffering
│ └── properties # Configuration properties binding
├── convert # Type conversion utilities
├── diagnostics # Failure analysis framework
├── env # Environment and property sources
├── info # Application runtime information
├── io # Resource loading
├── json # JSON parsing and writing
├── logging # Logging system configuration
├── retry # Retry policy support
├── ssl # SSL/TLS bundle management
├── system # System utilities (PID, home, temp)
├── task # Task executor builders
├── thread # Threading model detection
├── util # General utilities
├── web # Web application support
│ ├── context # Web application contexts
│ ├── error # Error page support
│ ├── server # Web server abstraction
│ └── servlet # Servlet registration
└── SpringApplication # Main entry pointSee: configuration-annotations.md
See: configuration-properties.md See: configuration-data.md
See: environment-property-sources.md
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.DefaultValue;
import java.time.Duration;
@ConfigurationProperties(prefix = "myapp")
public class MyAppProperties {
private String name;
private Duration timeout = Duration.ofSeconds(30);
private int maxRetries = 3;
// Getters and setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Duration getTimeout() { return timeout; }
public void setTimeout(Duration timeout) { this.timeout = timeout; }
public int getMaxRetries() { return maxRetries; }
public void setMaxRetries(int maxRetries) { this.maxRetries = maxRetries; }
}import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class StartupListener {
@EventListener(ApplicationReadyEvent.class)
public void onReady() {
System.out.println("Application is ready to serve traffic");
}
}import org.springframework.boot.availability.AvailabilityChangeEvent;
import org.springframework.boot.availability.LivenessState;
import org.springframework.boot.availability.ReadinessState;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
@Component
public class HealthManager {
private final ApplicationEventPublisher publisher;
public HealthManager(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
public void markAsReady() {
AvailabilityChangeEvent.publish(publisher, this, ReadinessState.ACCEPTING_TRAFFIC);
}
public void markAsUnhealthy() {
AvailabilityChangeEvent.publish(publisher, this, LivenessState.BROKEN);
}
}Production-ready application demonstrating configuration properties binding and lifecycle management:
package com.example.app;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import java.time.Duration;
/**
* Main application class with configuration properties.
*/
@SpringBootApplication
@EnableConfigurationProperties(AppProperties.class)
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
/**
* Type-safe configuration properties with validation.
*/
@ConfigurationProperties(prefix = "app")
public class AppProperties {
@NotBlank
private String name;
@Min(1)
private int maxConnections = 10;
private Duration timeout = Duration.ofSeconds(30);
private Database database = new Database();
private Cache cache = new Cache();
// Getters and setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getMaxConnections() { return maxConnections; }
public void setMaxConnections(int maxConnections) {
this.maxConnections = maxConnections;
}
public Duration getTimeout() { return timeout; }
public void setTimeout(Duration timeout) { this.timeout = timeout; }
public Database getDatabase() { return database; }
public void setDatabase(Database database) { this.database = database; }
public Cache getCache() { return cache; }
public void setCache(Cache cache) { this.cache = cache; }
public static class Database {
@NotBlank
private String url;
private String username;
private String password;
private int maxPoolSize = 20;
// Getters and setters
public String getUrl() { return url; }
public void setUrl(String url) { this.url = url; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public int getMaxPoolSize() { return maxPoolSize; }
public void setMaxPoolSize(int maxPoolSize) {
this.maxPoolSize = maxPoolSize;
}
}
public static class Cache {
private boolean enabled = true;
private Duration ttl = Duration.ofMinutes(10);
private int maxSize = 1000;
// Getters and setters
public boolean isEnabled() { return enabled; }
public void setEnabled(boolean enabled) { this.enabled = enabled; }
public Duration getTtl() { return ttl; }
public void setTtl(Duration ttl) { this.ttl = ttl; }
public int getMaxSize() { return maxSize; }
public void setMaxSize(int maxSize) { this.maxSize = maxSize; }
}
}
/**
* Service using configuration properties.
*/
@Service
public class ApplicationService {
private final AppProperties properties;
public ApplicationService(AppProperties properties) {
this.properties = properties;
}
@EventListener(ApplicationReadyEvent.class)
public void onApplicationReady() {
System.out.println("Application Ready: " + properties.getName());
System.out.println("Max Connections: " + properties.getMaxConnections());
System.out.println("Timeout: " + properties.getTimeout());
System.out.println("Database URL: " + properties.getDatabase().getUrl());
System.out.println("Cache Enabled: " + properties.getCache().isEnabled());
}
public void performOperation() {
// Use properties for business logic
if (properties.getCache().isEnabled()) {
System.out.println("Using cache with TTL: " + properties.getCache().getTtl());
}
}
}Configuration file (application.yml):
app:
name: MyApplication
max-connections: 50
timeout: 60s
database:
url: jdbc:postgresql://localhost:5432/mydb
username: admin
password: secret
max-pool-size: 30
cache:
enabled: true
ttl: 15m
max-size: 5000Complete event listener system covering all application lifecycle phases:
package com.example.lifecycle;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.availability.AvailabilityChangeEvent;
import org.springframework.boot.availability.LivenessState;
import org.springframework.boot.availability.ReadinessState;
import org.springframework.boot.context.event.*;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import java.time.Instant;
@SpringBootApplication
public class LifecycleApplication {
public static void main(String[] args) {
SpringApplication.run(LifecycleApplication.class, args);
}
}
/**
* Comprehensive lifecycle event listener demonstrating all major events.
*/
@Component
public class ApplicationLifecycleListener {
private final ApplicationEventPublisher publisher;
private Instant startTime;
public ApplicationLifecycleListener(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
@EventListener
public void onApplicationStartingEvent(ApplicationStartingEvent event) {
// Very early in startup - before Spring context is created
// Limited functionality available
startTime = Instant.now();
System.out.println("1. ApplicationStartingEvent - Bootstrap starting");
}
@EventListener
public void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
// Environment is ready, but context not yet created
// Can access/modify environment here
System.out.println("2. ApplicationEnvironmentPreparedEvent - Environment ready");
System.out.println(" Active profiles: " +
String.join(",", event.getEnvironment().getActiveProfiles()));
}
@EventListener
public void onApplicationContextInitializedEvent(ApplicationContextInitializedEvent event) {
// Context created but not yet refreshed
System.out.println("3. ApplicationContextInitializedEvent - Context initialized");
}
@EventListener
public void onApplicationPreparedEvent(ApplicationPreparedEvent event) {
// Context prepared and bean definitions loaded, but not yet refreshed
System.out.println("4. ApplicationPreparedEvent - Context prepared, beans loading");
}
@EventListener
public void onApplicationStartedEvent(ApplicationStartedEvent event) {
// Context refreshed, application started but CommandLineRunners not yet called
System.out.println("5. ApplicationStartedEvent - Context refreshed");
// Now safe to publish availability events
AvailabilityChangeEvent.publish(publisher, this, LivenessState.CORRECT);
}
@EventListener
public void onApplicationReadyEvent(ApplicationReadyEvent event) {
// Application fully started and ready to serve requests
Instant now = Instant.now();
long startupTime = now.toEpochMilli() - startTime.toEpochMilli();
System.out.println("6. ApplicationReadyEvent - Application READY");
System.out.println(" Startup time: " + startupTime + "ms");
// Mark application as ready to accept traffic
AvailabilityChangeEvent.publish(publisher, this, ReadinessState.ACCEPTING_TRAFFIC);
// Perform post-startup initialization
performPostStartupTasks();
}
@EventListener
public void onApplicationFailedEvent(ApplicationFailedEvent event) {
// Application failed to start
System.err.println("7. ApplicationFailedEvent - Startup FAILED");
System.err.println(" Error: " + event.getException().getMessage());
// Mark application as broken
AvailabilityChangeEvent.publish(publisher, this, LivenessState.BROKEN);
}
@EventListener
public void onContextClosedEvent(ContextClosedEvent event) {
// Application is shutting down
System.out.println("8. ContextClosedEvent - Application shutting down");
// Mark application as refusing traffic
AvailabilityChangeEvent.publish(publisher, this, ReadinessState.REFUSING_TRAFFIC);
// Perform cleanup
performShutdownTasks();
}
@EventListener
public void onAvailabilityChangeEvent(AvailabilityChangeEvent<?> event) {
// Availability state changed
System.out.println(" AvailabilityChangeEvent: " +
event.getState().getClass().getSimpleName() + " -> " + event.getState());
}
private void performPostStartupTasks() {
// Warm up caches, establish connections, etc.
System.out.println(" Performing post-startup initialization...");
}
private void performShutdownTasks() {
// Flush buffers, close connections, etc.
System.out.println(" Performing graceful shutdown tasks...");
}
}Production-ready failure analyzer for custom error handling:
package com.example.diagnostics;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
import org.springframework.boot.diagnostics.FailureAnalysis;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
import jakarta.annotation.PostConstruct;
@SpringBootApplication
public class DiagnosticsApplication {
public static void main(String[] args) {
SpringApplication.run(DiagnosticsApplication.class, args);
}
}
/**
* Custom exception for configuration errors.
*/
class InvalidConfigurationException extends RuntimeException {
private final String configKey;
private final String providedValue;
private final String expectedFormat;
public InvalidConfigurationException(String configKey, String providedValue,
String expectedFormat) {
super(String.format("Invalid configuration for '%s': '%s'", configKey, providedValue));
this.configKey = configKey;
this.providedValue = providedValue;
this.expectedFormat = expectedFormat;
}
public String getConfigKey() { return configKey; }
public String getProvidedValue() { return providedValue; }
public String getExpectedFormat() { return expectedFormat; }
}
/**
* Custom failure analyzer providing helpful error messages and solutions.
*/
public class InvalidConfigurationFailureAnalyzer
extends AbstractFailureAnalyzer<InvalidConfigurationException> {
@Override
protected FailureAnalysis analyze(Throwable rootFailure,
InvalidConfigurationException cause) {
StringBuilder description = new StringBuilder();
description.append("Application failed to start due to invalid configuration.\n\n");
description.append("Configuration Key: ").append(cause.getConfigKey()).append("\n");
description.append("Provided Value: ").append(cause.getProvidedValue()).append("\n");
description.append("Expected Format: ").append(cause.getExpectedFormat()).append("\n");
StringBuilder action = new StringBuilder();
action.append("Update your configuration:\n\n");
action.append("1. Check application.properties or application.yml\n");
action.append("2. Ensure '").append(cause.getConfigKey())
.append("' matches format: ").append(cause.getExpectedFormat()).append("\n");
action.append("3. Example: ").append(getExample(cause.getConfigKey())).append("\n");
action.append("\nFor more information, see: https://docs.example.com/config/")
.append(cause.getConfigKey().replace('.', '-'));
return new FailureAnalysis(description.toString(), action.toString(), cause);
}
private String getExample(String configKey) {
return switch (configKey) {
case "app.database.url" ->
"app.database.url=jdbc:postgresql://localhost:5432/mydb";
case "app.timeout" ->
"app.timeout=30s";
case "app.max-connections" ->
"app.max-connections=50";
default ->
configKey + "=<valid-value>";
};
}
}
/**
* Service that validates configuration during startup.
*/
@Service
class ConfigurationValidator {
private final String databaseUrl;
public ConfigurationValidator(@org.springframework.beans.factory.annotation.Value("${app.database.url}")
String databaseUrl) {
this.databaseUrl = databaseUrl;
}
@PostConstruct
public void validate() {
// Validate database URL format
if (!databaseUrl.startsWith("jdbc:")) {
throw new InvalidConfigurationException(
"app.database.url",
databaseUrl,
"jdbc:<database-type>://<host>:<port>/<database>"
);
}
System.out.println("Configuration validated successfully");
}
}Register the analyzer in META-INF/spring.factories:
org.springframework.boot.diagnostics.FailureAnalyzer=\
com.example.diagnostics.InvalidConfigurationFailureAnalyzerApplication runner with environment-specific initialization:
package com.example.runner;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Profile;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@SpringBootApplication
public class RunnerApplication {
public static void main(String[] args) {
SpringApplication.run(RunnerApplication.class, args);
}
}
/**
* Ordered runner that executes first - performs critical initialization.
*/
@Component
@Order(1)
class DatabaseInitializationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("1. Database Initialization Runner");
System.out.println(" Initializing database schema...");
System.out.println(" Creating indexes...");
// Access command-line arguments
if (args.containsOption("force-recreate")) {
System.out.println(" Force recreate enabled");
}
// Access non-option args
if (!args.getNonOptionArgs().isEmpty()) {
System.out.println(" Additional args: " + args.getNonOptionArgs());
}
}
}
/**
* Ordered runner that executes second - loads reference data.
*/
@Component
@Order(2)
class DataLoadingRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("2. Data Loading Runner");
System.out.println(" Loading reference data...");
System.out.println(" Command-line args: " + Arrays.toString(args));
}
}
/**
* Profile-specific runner for development environment.
*/
@Component
@Order(3)
@Profile("dev")
class DevelopmentDataRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("3. Development Data Runner (dev profile only)");
System.out.println(" Loading test users...");
System.out.println(" Loading sample data...");
}
}
/**
* Profile-specific runner for production environment.
*/
@Component
@Order(3)
@Profile("prod")
class ProductionVerificationRunner implements ApplicationRunner {
private final Environment environment;
public ProductionVerificationRunner(Environment environment) {
this.environment = environment;
}
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("3. Production Verification Runner (prod profile only)");
// Verify critical configuration
String dbUrl = environment.getProperty("app.database.url");
if (dbUrl == null || dbUrl.contains("localhost")) {
throw new IllegalStateException(
"Invalid database URL for production: " + dbUrl
);
}
System.out.println(" Production environment verified");
System.out.println(" Database: " + dbUrl);
}
}
/**
* Runner with error handling and retry logic.
*/
@Component
@Order(4)
class ExternalServiceHealthCheckRunner implements ApplicationRunner {
private static final int MAX_RETRIES = 3;
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("4. External Service Health Check Runner");
int attempt = 0;
boolean healthy = false;
while (attempt < MAX_RETRIES && !healthy) {
attempt++;
System.out.println(" Health check attempt " + attempt + "/" + MAX_RETRIES);
try {
// Simulate health check
Thread.sleep(1000);
healthy = checkExternalServices();
if (healthy) {
System.out.println(" All external services healthy");
}
} catch (Exception e) {
System.err.println(" Health check failed: " + e.getMessage());
if (attempt >= MAX_RETRIES) {
throw new IllegalStateException(
"External services not available after " + MAX_RETRIES + " attempts"
);
}
// Exponential backoff
Thread.sleep(1000L * attempt);
}
}
}
private boolean checkExternalServices() {
// Check external APIs, databases, message queues, etc.
return true;
}
}Run with arguments:
java -jar app.jar --force-recreate --spring.profiles.active=dev additional-argComplete SSL bundle management with certificate rotation:
package com.example.ssl;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.ssl.SslBundle;
import org.springframework.boot.ssl.SslBundles;
import org.springframework.boot.web.server.Ssl;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.util.concurrent.atomic.AtomicReference;
@SpringBootApplication
@EnableScheduling
public class SslApplication {
public static void main(String[] args) {
SpringApplication.run(SslApplication.class, args);
}
}
/**
* SSL configuration with hot reload support.
*/
@Component
class SslConfigurationManager {
private final SslBundles sslBundles;
private final AtomicReference<Instant> lastReload = new AtomicReference<>(Instant.now());
public SslConfigurationManager(SslBundles sslBundles) {
this.sslBundles = sslBundles;
}
/**
* Check for certificate updates every hour.
*/
@Scheduled(fixedRate = 3600000) // Every hour
public void checkCertificateExpiry() {
try {
SslBundle bundle = sslBundles.getBundle("server");
// Get certificate details (implementation depends on bundle type)
System.out.println("Checking SSL certificate status...");
System.out.println("Last reload: " + lastReload.get());
// Trigger reload if certificate is close to expiry or has been updated
// In production, check actual certificate expiry dates
boolean needsReload = shouldReloadCertificate(bundle);
if (needsReload) {
reloadCertificate();
}
} catch (Exception e) {
System.err.println("Failed to check certificate status: " + e.getMessage());
}
}
private boolean shouldReloadCertificate(SslBundle bundle) {
// Check if certificate file has been modified
// Check if certificate is within 30 days of expiry
// Implementation depends on your certificate management strategy
return false;
}
public void reloadCertificate() {
System.out.println("Reloading SSL certificate...");
try {
// Reload certificate from disk or certificate store
// This triggers bundle update listeners
SslBundle bundle = sslBundles.getBundle("server");
lastReload.set(Instant.now());
System.out.println("SSL certificate reloaded successfully at " + lastReload.get());
} catch (Exception e) {
System.err.println("Failed to reload certificate: " + e.getMessage());
throw new RuntimeException("Certificate reload failed", e);
}
}
}
/**
* Web server customizer for SSL configuration.
*/
@Component
class SslWebServerCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
private final SslBundles sslBundles;
public SslWebServerCustomizer(SslBundles sslBundles) {
this.sslBundles = sslBundles;
}
@Override
public void customize(ConfigurableServletWebServerFactory factory) {
try {
SslBundle bundle = sslBundles.getBundle("server");
Ssl ssl = new Ssl();
ssl.setEnabled(true);
// Configure SSL from bundle
factory.setSsl(ssl);
System.out.println("SSL configured from bundle 'server'");
} catch (Exception e) {
System.err.println("Failed to configure SSL: " + e.getMessage());
}
}
}Configuration (application.yml):
spring:
ssl:
bundle:
pem:
server:
keystore:
certificate: classpath:ssl/server.crt
private-key: classpath:ssl/server.key
truststore:
certificate: classpath:ssl/ca.crt
jks:
client:
keystore:
location: classpath:ssl/client-keystore.jks
password: changeit
type: JKS
truststore:
location: classpath:ssl/client-truststore.jks
password: changeit
server:
port: 8443
ssl:
enabled: true
bundle: serverConfiguration Properties Best Practices
@ConfigurationProperties for type-safe configuration instead of @Value@Validated and JSR-303 annotationsApplication Lifecycle Management
ApplicationReadyEvent for post-startup initialization, not @PostConstructApplicationFailedEvent for proper error reporting@Order to control ApplicationRunner/CommandLineRunner execution sequenceContextClosedEvent listenersError Handling and Diagnostics
FailureAnalyzer implementations for domain-specific errorsEnvironment-Specific Configuration
Dependency Injection Best Practices
@Autowired only for optional dependenciesfinal to ensure immutabilityObjectProvider<T> for lazy or optional bean resolutionLogging Configuration
Performance and Resource Management
spring.main.lazy-initialization=true) for faster startup in development@ConditionalOnProperty to disable features not needed in certain environmentsBufferingApplicationStartup and optimize bottlenecksSecurity Configuration
spring.ssl.bundle)SslBundles hot reloadTesting Strategies
@SpringBootTest for full integration tests@TestPropertySource to override configuration in tests@MockBean for unit testsOutputCaptureExtension for log assertionsProduction Readiness
spring-boot-maven-plugin with build-info goal) for version trackingProblem: @ConfigurationProperties class not registered as a bean
Error:
Parameter 0 of constructor in com.example.MyService required a bean of type 'com.example.AppProperties' that could not be found.Solution:
// Solution 1: Enable in main application class
@SpringBootApplication
@EnableConfigurationProperties(AppProperties.class)
public class MyApplication {
// ...
}
// Solution 2: Add @Component to properties class
@Component
@ConfigurationProperties(prefix = "app")
public class AppProperties {
// ...
}
// Solution 3: Use @ConfigurationPropertiesScan
@SpringBootApplication
@ConfigurationPropertiesScan("com.example.config")
public class MyApplication {
// ...
}Rationale: Spring Boot requires explicit registration of @ConfigurationProperties classes. Without registration, the class is not instantiated as a bean and cannot be injected into other components.
Problem: Properties not binding due to naming mismatch
Error: Properties remain null despite being set in application.properties
Solution:
# WRONG - uses dots in compound words
app.maxconnections=50
app.database.maxpoolsize=20
# CORRECT - use kebab-case (recommended)
app.max-connections=50
app.database.max-pool-size=20
# Also works - camelCase
app.maxConnections=50
app.database.maxPoolSize=20// Java property names use camelCase
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private int maxConnections; // Binds to app.max-connections or app.maxConnections
public int getMaxConnections() { return maxConnections; }
public void setMaxConnections(int maxConnections) {
this.maxConnections = maxConnections;
}
}Rationale: Spring Boot uses relaxed binding to map property names. Use kebab-case in properties files (recommended) or camelCase. Always use camelCase in Java code. Spring Boot automatically converts between formats.
Problem: Using wrong interface or expecting wrong argument format
Error: Cannot access parsed command-line options
Solution:
// Use ApplicationRunner when you need parsed arguments
@Component
class MyApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
// Access options: --option=value
if (args.containsOption("debug")) {
System.out.println("Debug mode enabled");
}
// Access non-option args
List<String> nonOptionArgs = args.getNonOptionArgs();
}
}
// Use CommandLineRunner when you need raw String[] args
@Component
class MyCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
// Access raw arguments
System.out.println("Args: " + Arrays.toString(args));
}
}Rationale: ApplicationRunner provides parsed arguments via ApplicationArguments, making it easier to access named options. CommandLineRunner provides raw String[] array. Use ApplicationRunner unless you specifically need raw access.
Problem: Accessing beans or context too early in lifecycle
Error:
BeanCreationNotAllowedException: Error creating bean with name 'myBean'
NullPointerException when accessing ApplicationContextSolution:
// WRONG - Too early, context not ready
@Component
public class EarlyInitializer {
private final ApplicationContext context;
public EarlyInitializer(ApplicationContext context) {
// DON'T access beans in constructor
context.getBean(MyService.class); // MAY FAIL
}
}
// CORRECT - Use appropriate lifecycle callback
@Component
public class ProperInitializer {
@Autowired
private MyService myService; // Injected after construction
@PostConstruct
public void init() {
// Safe to use injected beans here
myService.initialize();
}
@EventListener(ApplicationReadyEvent.class)
public void onReady() {
// Safest - application fully started
myService.performOperation();
}
}Rationale: Constructor execution happens during bean creation, before all dependencies are wired. Use @PostConstruct for bean initialization or ApplicationReadyEvent for application-wide initialization after full startup.
Problem: Invalid configuration accepted without errors
Error: Application starts with invalid configuration, fails later at runtime
Solution:
import jakarta.validation.constraints.*;
@Validated // REQUIRED for validation to work
@ConfigurationProperties(prefix = "app")
public class AppProperties {
@NotBlank(message = "Application name is required")
private String name;
@Min(value = 1, message = "Max connections must be at least 1")
@Max(value = 100, message = "Max connections cannot exceed 100")
private int maxConnections = 10;
@Pattern(regexp = "^jdbc:.*", message = "Database URL must start with 'jdbc:'")
private String databaseUrl;
@NotNull
@DurationMin(seconds = 1)
@DurationMax(minutes = 5)
private Duration timeout;
// Getters and setters with validation
}Configuration to trigger validation failure:
app.name=
app.max-connections=150
app.database-url=invalid-url
app.timeout=10mRationale: Without @Validated annotation on the properties class, JSR-303 validation annotations are ignored. Application accepts invalid configuration and fails unpredictably later. Always validate critical configuration.
Problem: ApplicationRunner depends on beans that depend on runner completion
Error:
The dependencies of some of the beans in the application context form a cycleSolution:
// WRONG - Circular dependency
@Component
class DataLoader implements ApplicationRunner {
private final MyService service; // MyService depends on DataLoader completion
@Override
public void run(ApplicationArguments args) {
service.loadData(); // Circular!
}
}
// CORRECT - Break circular dependency
@Component
class DataLoader implements ApplicationRunner {
private final ApplicationContext context;
public DataLoader(ApplicationContext context) {
this.context = context;
}
@Override
public void run(ApplicationArguments args) {
// Lazy lookup breaks circular dependency
MyService service = context.getBean(MyService.class);
service.loadData();
}
}
// BETTER - Use event listener instead
@Component
class DataLoader {
private final MyService service;
public DataLoader(MyService service) {
this.service = service;
}
@EventListener(ApplicationReadyEvent.class)
public void onReady() {
service.loadData();
}
}Rationale: ApplicationRunner beans are created during startup, which can create circular dependencies. Use lazy lookup via ApplicationContext or switch to @EventListener(ApplicationReadyEvent.class) which fires after all beans are created.
Problem: Application identified as "application" in logs and monitoring
Error: Cannot distinguish between multiple applications in distributed systems
Solution:
# application.properties
spring.application.name=my-service
# Or application.yml
spring:
application:
name: my-service// Access programmatically
@Component
public class AppNameChecker {
@Value("${spring.application.name}")
private String applicationName;
@PostConstruct
public void checkName() {
if ("application".equals(applicationName)) {
throw new IllegalStateException(
"Application name not configured. Set spring.application.name in properties"
);
}
}
}Rationale: Default application name is "application", which makes it impossible to identify services in logs, metrics, and distributed tracing. Always set a unique application name for each service.
Problem: SSL certificate not loaded or server won't start with SSL
Error:
java.io.FileNotFoundException: class path resource [ssl/server.crt] cannot be opened
SSL handshake failedSolution:
# Correct PEM configuration
spring:
ssl:
bundle:
pem:
server:
keystore:
certificate: "file:/etc/ssl/certs/server.crt" # Use file: for filesystem
private-key: "file:/etc/ssl/private/server.key"
truststore:
certificate: "file:/etc/ssl/certs/ca.crt"
# Or use classpath (must be in resources/)
spring:
ssl:
bundle:
pem:
server:
keystore:
certificate: "classpath:ssl/server.crt"
private-key: "classpath:ssl/server.key"
# Enable SSL on server
server:
port: 8443
ssl:
enabled: true
bundle: server// Verify SSL configuration at startup
@Component
public class SslConfigurationValidator {
@Value("${server.ssl.enabled:false}")
private boolean sslEnabled;
@Value("${server.ssl.bundle:}")
private String sslBundle;
@PostConstruct
public void validate() {
if (sslEnabled && sslBundle.isEmpty()) {
throw new IllegalStateException(
"SSL enabled but no bundle configured. Set server.ssl.bundle"
);
}
}
}Rationale: SSL bundle configuration requires correct resource prefix (file: or classpath:) and valid certificate files. Without proper configuration, server fails to start with cryptic SSL errors. Always validate SSL configuration at startup.
Problem: Application fails silently or with unhelpful error messages
Error: Startup fails but root cause is buried in stack trace
Solution:
@Component
public class StartupFailureHandler {
@EventListener
public void onApplicationFailed(ApplicationFailedEvent event) {
Throwable exception = event.getException();
System.err.println("=================================");
System.err.println("APPLICATION FAILED TO START");
System.err.println("=================================");
System.err.println("Reason: " + exception.getMessage());
System.err.println("Exception type: " + exception.getClass().getName());
// Log specific failure types
if (exception instanceof BeanCreationException) {
System.err.println("Bean creation failed - check configuration and dependencies");
} else if (exception instanceof BindException) {
System.err.println("Configuration binding failed - check properties");
} else if (exception instanceof DataAccessException) {
System.err.println("Database connection failed - check database configuration");
}
System.err.println("\nFull stack trace:");
exception.printStackTrace();
// Optionally send alerts
sendStartupFailureAlert(exception);
}
private void sendStartupFailureAlert(Throwable exception) {
// Send to monitoring system, Slack, PagerDuty, etc.
}
}Rationale: Default Spring Boot error messages can be difficult to diagnose. Listening to ApplicationFailedEvent allows custom error handling, better logging, and alerting. Essential for production troubleshooting.
Problem: Properties from wrong source taking effect
Error: Expected property value not being used
Solution:
// Understand property source precedence (highest to lowest):
// 1. Command line arguments
// 2. SPRING_APPLICATION_JSON properties
// 3. ServletConfig init parameters
// 4. ServletContext init parameters
// 5. JNDI attributes from java:comp/env
// 6. Java System properties (System.getProperties())
// 7. OS environment variables
// 8. Profile-specific application properties (application-{profile}.properties)
// 9. Application properties (application.properties)
// 10. @PropertySource annotations
// 11. Default properties (SpringApplication.setDefaultProperties)
// Example: Debug property sources
@Component
public class PropertySourceDebugger {
private final ConfigurableEnvironment environment;
public PropertySourceDebugger(ConfigurableEnvironment environment) {
this.environment = environment;
}
@EventListener(ApplicationReadyEvent.class)
public void debugPropertySources() {
String propertyKey = "app.database.url";
String value = environment.getProperty(propertyKey);
System.out.println("\n=== Property Source Debug ===");
System.out.println("Property: " + propertyKey);
System.out.println("Final value: " + value);
System.out.println("\nProperty sources (in precedence order):");
environment.getPropertySources().forEach(source -> {
if (source instanceof EnumerablePropertySource) {
EnumerablePropertySource<?> enumerable = (EnumerablePropertySource<?>) source;
for (String name : enumerable.getPropertyNames()) {
if (name.equals(propertyKey)) {
System.out.println(" " + source.getName() + ": " +
enumerable.getProperty(name));
}
}
}
});
}
}# Override in specific environments
# application.properties (default)
app.database.url=jdbc:postgresql://localhost:5432/devdb
# application-prod.properties (production override)
app.database.url=jdbc:postgresql://prod-db:5432/proddb
# Can also override via environment variable
# APP_DATABASE_URL=jdbc:postgresql://override-db:5432/db
# Or command line (highest precedence)
# java -jar app.jar --app.database.url=jdbc:postgresql://cmdline-db:5432/dbRationale: Spring Boot has complex property precedence rules. Properties can be overridden from multiple sources, leading to unexpected values. Use environment-specific property files and understand precedence order to avoid conflicts.
// Core application
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.boot.autoconfigure.SpringBootApplication;
// Configuration
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.context.config.*;
// Lifecycle
import org.springframework.boot.context.event.*;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.CommandLineRunner;
// Availability
import org.springframework.boot.availability.*;
// Logging
import org.springframework.boot.logging.*;
// SSL/TLS
import org.springframework.boot.ssl.*;
// JSON
import org.springframework.boot.json.*;
// Info
import org.springframework.boot.info.*;
// Cloud
import org.springframework.boot.cloud.*;
// Conversion
import org.springframework.boot.convert.*;
// Diagnostics
import org.springframework.boot.diagnostics.*;
// System
import org.springframework.boot.system.*;
// Task execution
import org.springframework.boot.task.*;
// Web
import org.springframework.boot.web.server.*;
import org.springframework.boot.web.servlet.*;
import org.springframework.boot.web.error.*;