Core library providing essential Spring Boot functionality for bootstrapping, configuration, logging, and application lifecycle management.
The 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.*;Install with Tessl CLI
npx tessl i tessl/maven-org-springframework-boot--spring-boot