docs
Package: org.springframework.boot
Primary Class: SpringApplication
Core functionality for starting and configuring Spring Boot applications. Provides both simple static methods for quick startup and extensive configuration options for production use.
// Simplest possible startup
SpringApplication.run(MyApplication.class, args);
// With configuration
SpringApplication app = new SpringApplication(MyApplication.class);
app.setWebApplicationType(WebApplicationType.SERVLET);
app.setBannerMode(Banner.Mode.OFF);
app.run(args);| Class | Purpose | Thread Safety |
|---|---|---|
SpringApplication | Main bootstrapping class | Not thread-safe |
SpringApplicationBuilder | Fluent API builder | Not thread-safe |
WebApplicationType | Web application type enum | Immutable |
ApplicationRunner | Post-startup callback (parsed args) | N/A |
CommandLineRunner | Post-startup callback (raw args) | N/A |
BootstrapRegistry | Early-stage object registry | Thread-safe |
ExitCodeGenerator | Exit code provider | N/A |
package org.springframework.boot;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.ResourceLoader;
import java.util.Collection;
import java.util.Set;
/**
* Main class for bootstrapping and launching a Spring application from a Java main method.
*/
public class SpringApplication {
// Constants
public static final String BANNER_LOCATION_PROPERTY = "spring.banner.location";
public static final String BANNER_LOCATION_PROPERTY_VALUE = "banner.txt";
// Constructors
public SpringApplication(Class<?>... primarySources);
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources);
// Static run methods - returns ConfigurableApplicationContext
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args);
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args);
// Static main method - convenience entry point
public static void main(String[] args) throws Exception;
// Instance run method
public ConfigurableApplicationContext run(String... args);
// Static utility methods
public static int exit(ApplicationContext context, ExitCodeGenerator... exitCodeGenerators);
public static SpringApplicationShutdownHandlers getShutdownHandlers();
public static SpringApplication.Augmented from(ThrowingConsumer<String[]> main);
public static void withHook(SpringApplicationHook hook, Runnable action);
public static <T> T withHook(SpringApplicationHook hook, ThrowingSupplier<T> action);
// Configuration methods (return void - method chaining not supported)
public void setWebApplicationType(WebApplicationType webApplicationType);
public void setAllowBeanDefinitionOverriding(boolean allowBeanDefinitionOverriding);
public void setAllowCircularReferences(boolean allowCircularReferences);
public void setLazyInitialization(boolean lazyInitialization);
public void setHeadless(boolean headless);
public void setRegisterShutdownHook(boolean registerShutdownHook);
public void setBanner(Banner banner);
public void setBannerMode(Banner.Mode bannerMode);
public void setLogStartupInfo(boolean logStartupInfo);
public void setAddCommandLineProperties(boolean addCommandLineProperties);
public void setAddConversionService(boolean addConversionService);
public void setDefaultProperties(Map<String, Object> defaultProperties);
public void setDefaultProperties(Properties defaultProperties);
public void setAdditionalProfiles(String... profiles);
public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator);
public void setEnvironment(@Nullable ConfigurableEnvironment environment);
public void setEnvironmentPrefix(String environmentPrefix);
public void setApplicationContextFactory(@Nullable ApplicationContextFactory applicationContextFactory);
public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers);
public void addInitializers(ApplicationContextInitializer<?>... initializers);
public void setListeners(Collection<? extends ApplicationListener<?>> listeners);
public void addListeners(ApplicationListener<?>... listeners);
public void setApplicationStartup(ApplicationStartup applicationStartup);
public void setKeepAlive(boolean keepAlive);
public void addBootstrapRegistryInitializer(BootstrapRegistryInitializer bootstrapRegistryInitializer);
public void addPrimarySources(Collection<Class<?>> additionalPrimarySources);
public void setSources(Set<String> sources);
public void setResourceLoader(ResourceLoader resourceLoader);
public void setMainApplicationClass(@Nullable Class<?> mainApplicationClass);
// Getters
public @Nullable WebApplicationType getWebApplicationType();
public Set<String> getAdditionalProfiles();
public @Nullable String getEnvironmentPrefix();
public Set<ApplicationContextInitializer<?>> getInitializers();
public Set<ApplicationListener<?>> getListeners();
public ApplicationStartup getApplicationStartup();
public boolean isKeepAlive();
public Set<String> getSources();
public Set<Object> getAllSources();
public @Nullable ResourceLoader getResourceLoader();
public ClassLoader getClassLoader();
public @Nullable Class<?> getMainApplicationClass();
}IMPORTANT: Most setter methods return void, NOT SpringApplication. For method chaining, use SpringApplicationBuilder instead.
// This DOES NOT work - setters return void
SpringApplication app = new SpringApplication(MyApp.class)
.setWebApplicationType(WebApplicationType.SERVLET) // Compilation error!
.setBannerMode(Banner.Mode.OFF);
// Correct approaches:
// Option 1: Multiple statements
SpringApplication app = new SpringApplication(MyApp.class);
app.setWebApplicationType(WebApplicationType.SERVLET);
app.setBannerMode(Banner.Mode.OFF);
app.run(args);
// Option 2: Use SpringApplicationBuilder for method chaining
new SpringApplicationBuilder(MyApp.class)
.web(WebApplicationType.SERVLET)
.bannerMode(Banner.Mode.OFF)
.run(args);
// Option 3: Use SpringApplication.main() for command-line launch
// When --spring.main.sources is provided, launches those sources
// Usage: java -cp app.jar org.springframework.boot.SpringApplication \
// --spring.main.sources=com.example.MyApp arg1 arg2The static main() method provides a convenient way to launch Spring Boot applications directly from the SpringApplication class without writing a custom main method:
// Can be launched via command line:
// java -cp myapp.jar org.springframework.boot.SpringApplication \
// --spring.main.sources=com.example.MyApplication \
// --server.port=8080
// Or programmatically
String[] args = {
"--spring.main.sources=com.example.MyApplication",
"--server.port=9000"
};
SpringApplication.main(args);This is useful for:
main(String[]) signatureNote: The --spring.main.sources argument specifies which configuration classes to load.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.Banner;
public class MyApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MyApplication.class);
// Web application type
app.setWebApplicationType(WebApplicationType.SERVLET); // or REACTIVE, or NONE
// Banner configuration
app.setBannerMode(Banner.Mode.OFF); // OFF, CONSOLE, or LOG
// Startup logging
app.setLogStartupInfo(true); // Log startup information
// Run the application
ConfigurableApplicationContext context = app.run(args);
}
}import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.Configuration;
// Main application class
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(
MyApplication.class,
AdditionalConfig.class,
DatabaseConfig.class
);
app.run(args);
}
}
// Additional configuration classes
@Configuration
class AdditionalConfig {
// Additional beans
}
@Configuration
class DatabaseConfig {
// Database-specific beans
}import org.springframework.boot.SpringApplication;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.env.MapPropertySource;
import java.util.HashMap;
import java.util.Map;
public class MyApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MyApplication.class);
// Create custom environment
StandardEnvironment env = new StandardEnvironment();
// Add custom property source
Map<String, Object> props = new HashMap<>();
props.put("custom.property", "value");
props.put("server.port", "8081");
env.getPropertySources().addFirst(
new MapPropertySource("customProps", props)
);
// Set active profiles
env.setActiveProfiles("production", "aws");
// Apply environment to application
app.setEnvironment(env);
app.run(args);
}
}import org.springframework.boot.SpringApplication;
public class MyApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MyApplication.class);
// Allow bean definition overriding (default: false)
// Set to true if you need to override auto-configured beans
app.setAllowBeanDefinitionOverriding(true);
// Control circular references (default: false)
// Generally recommended to keep false and refactor circular dependencies
app.setAllowCircularReferences(false);
app.run(args);
}
}import org.springframework.boot.SpringApplication;
public class MyApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MyApplication.class);
// Enable lazy initialization for faster startup
// Beans are created on first use instead of at startup
app.setLazyInitialization(true);
app.run(args);
}
}Note: When using lazy initialization, consider excluding specific beans:
import org.springframework.boot.LazyInitializationExcludeFilter;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class LazyConfig {
@Bean
public LazyInitializationExcludeFilter eagerBeanFilter() {
return (String beanName, BeanDefinition beanDef, Class<?> beanType) -> {
// Eagerly initialize beans that implement specific interface
return InitializationRequired.class.isAssignableFrom(beanType);
};
}
}import org.springframework.boot.SpringApplication;
import org.springframework.context.ApplicationListener;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
public class MyApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MyApplication.class);
// Add application listeners
app.addListeners((ApplicationListener<ApplicationReadyEvent>) event -> {
System.out.println("Application is ready!");
});
// Add context initializers
app.addInitializers(
(ApplicationContextInitializer<ConfigurableApplicationContext>) context -> {
// Customize context before refresh
System.out.println("Initializing context: " + context.getId());
}
);
app.run(args);
}
}import org.springframework.boot.SpringApplication;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
public class MyApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MyApplication.class);
// Option 1: Using Map
Map<String, Object> defaultProps = new HashMap<>();
defaultProps.put("server.port", 8080);
defaultProps.put("spring.application.name", "my-app");
defaultProps.put("logging.level.root", "INFO");
app.setDefaultProperties(defaultProps);
// Option 2: Using Properties
Properties props = new Properties();
props.setProperty("server.port", "8080");
props.setProperty("spring.application.name", "my-app");
app.setDefaultProperties(props);
app.run(args);
}
}import org.springframework.boot.SpringApplication;
public class MyApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MyApplication.class);
// Set additional profiles beyond those specified in properties/args
app.setAdditionalProfiles("debug", "metrics");
app.run(args);
}
}import org.springframework.boot.SpringApplication;
import org.springframework.boot.ExitCodeGenerator;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;
public class MyApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context =
SpringApplication.run(MyApplication.class, args);
// Exit with code from ExitCodeGenerators
int exitCode = SpringApplication.exit(context,
() -> 0, // Success
() -> 1 // Additional generator
);
System.exit(exitCode);
}
}
// ExitCodeGenerator component
@Component
public class ApplicationExitCodeGenerator implements ExitCodeGenerator {
private int exitCode = 0;
public void setExitCode(int exitCode) {
this.exitCode = exitCode;
}
@Override
public int getExitCode() {
return this.exitCode;
}
}import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationShutdownHandlers;
public class MyApplication {
public static void main(String[] args) {
// Get shutdown handlers registry
SpringApplicationShutdownHandlers handlers =
SpringApplication.getShutdownHandlers();
// Add custom shutdown action
Runnable shutdownAction = () -> {
System.out.println("Performing cleanup...");
// Cleanup code
};
handlers.add(shutdownAction);
// Run application
SpringApplication.run(MyApplication.class, args);
// Later, can remove shutdown action if needed
handlers.remove(shutdownAction);
}
}Note: Shutdown handlers run sequentially AFTER application contexts have closed.
Interface for adding or removing shutdown handlers that execute when the JVM exits. Unlike JVM shutdown hooks (which run concurrently), Spring Boot shutdown handlers run sequentially and are guaranteed to execute only after all ApplicationContext instances have been closed.
package org.springframework.boot;
/**
* Interface that can be used to add or remove code that should run when the JVM is
* shutdown. Shutdown handlers run sequentially rather than concurrently.
* Handlers are guaranteed to be called only after registered ApplicationContext
* instances have been closed.
*
* @since 2.5.1
*/
public interface SpringApplicationShutdownHandlers {
/**
* Add an action to the handlers that will be run when the JVM exits.
*
* @param action the action to add
*/
void add(Runnable action);
/**
* Remove a previously added action so that it no longer runs when the JVM exits.
*
* @param action the action to remove
*/
void remove(Runnable action);
}Obtain the shutdown handlers registry using the static method:
SpringApplicationShutdownHandlers handlers = SpringApplication.getShutdownHandlers();| Feature | SpringApplicationShutdownHandlers | Runtime.addShutdownHook() |
|---|---|---|
| Execution order | Sequential (ordered) | Concurrent (unordered) |
| When executed | After ApplicationContext closed | JVM shutdown (arbitrary timing) |
| Thread safety | Built-in | Manual synchronization required |
| Context access | Contexts already closed | Contexts may be closing |
| Ordering control | Yes (add order) | No |
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationShutdownHandlers;
public class MyApplication {
public static void main(String[] args) {
// Get shutdown handlers
SpringApplicationShutdownHandlers handlers =
SpringApplication.getShutdownHandlers();
// Add cleanup action
handlers.add(() -> {
System.out.println("Performing cleanup...");
// Close connections, flush buffers, etc.
});
// Run application
SpringApplication.run(MyApplication.class, args);
}
}import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationShutdownHandlers;
public class ResourceManager {
private final Runnable cleanupAction;
public ResourceManager() {
this.cleanupAction = () -> {
System.out.println("Releasing resources...");
// Cleanup logic
};
// Register cleanup on startup
SpringApplicationShutdownHandlers handlers =
SpringApplication.getShutdownHandlers();
handlers.add(cleanupAction);
}
public void close() {
// Remove handler if resource closed early
SpringApplicationShutdownHandlers handlers =
SpringApplication.getShutdownHandlers();
handlers.remove(cleanupAction);
}
}import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationShutdownHandlers;
public class MyApplication {
public static void main(String[] args) {
SpringApplicationShutdownHandlers handlers =
SpringApplication.getShutdownHandlers();
// Handlers execute in registration order
handlers.add(() -> {
System.out.println("1. Flushing logs...");
});
handlers.add(() -> {
System.out.println("2. Closing database connections...");
});
handlers.add(() -> {
System.out.println("3. Sending shutdown notification...");
});
SpringApplication.run(MyApplication.class, args);
// On JVM shutdown, output will be:
// 1. Flushing logs...
// 2. Closing database connections...
// 3. Sending shutdown notification...
}
}Fluent API for building and customizing SpringApplication with support for hierarchical contexts.
package org.springframework.boot.builder;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.io.ResourceLoader;
/**
* Builder for SpringApplication with convenient fluent API and context hierarchy support.
*/
public class SpringApplicationBuilder {
// Constructors
public SpringApplicationBuilder(Class<?>... sources);
public SpringApplicationBuilder(ResourceLoader resourceLoader, Class<?>... sources);
// Build and run (terminal operations)
public SpringApplication build();
public SpringApplication build(String... args);
public ConfigurableApplicationContext run(String... args);
// Access current state
public SpringApplication application();
public ConfigurableApplicationContext context();
// Hierarchy methods
public SpringApplicationBuilder child(Class<?>... sources);
public SpringApplicationBuilder parent(Class<?>... sources);
public SpringApplicationBuilder parent(ConfigurableApplicationContext parent);
public SpringApplicationBuilder sibling(Class<?>... sources);
public SpringApplicationBuilder sibling(Class<?>[] sources, String... args);
// Fluent configuration methods (all return SpringApplicationBuilder for chaining)
public SpringApplicationBuilder contextFactory(ApplicationContextFactory factory);
public SpringApplicationBuilder sources(Class<?>... sources);
public SpringApplicationBuilder web(WebApplicationType webApplicationType);
public SpringApplicationBuilder logStartupInfo(boolean logStartupInfo);
public SpringApplicationBuilder banner(Banner banner);
public SpringApplicationBuilder bannerMode(Banner.Mode bannerMode);
public SpringApplicationBuilder headless(boolean headless);
public SpringApplicationBuilder registerShutdownHook(boolean registerShutdownHook);
public SpringApplicationBuilder main(Class<?> mainApplicationClass);
public SpringApplicationBuilder addCommandLineProperties(boolean addCommandLineProperties);
public SpringApplicationBuilder setAddConversionService(boolean addConversionService);
public SpringApplicationBuilder addBootstrapRegistryInitializer(
BootstrapRegistryInitializer bootstrapRegistryInitializer);
public SpringApplicationBuilder lazyInitialization(boolean lazyInitialization);
public SpringApplicationBuilder properties(String... defaultProperties);
public SpringApplicationBuilder properties(Properties defaultProperties);
public SpringApplicationBuilder properties(Map<String, Object> defaults);
public SpringApplicationBuilder profiles(String... profiles);
public SpringApplicationBuilder beanNameGenerator(BeanNameGenerator beanNameGenerator);
public SpringApplicationBuilder environment(ConfigurableEnvironment environment);
public SpringApplicationBuilder environmentPrefix(String environmentPrefix);
public SpringApplicationBuilder resourceLoader(ResourceLoader resourceLoader);
public SpringApplicationBuilder initializers(ApplicationContextInitializer<?>... initializers);
public SpringApplicationBuilder listeners(ApplicationListener<?>... listeners);
public SpringApplicationBuilder applicationStartup(ApplicationStartup applicationStartup);
public SpringApplicationBuilder allowCircularReferences(boolean allowCircularReferences);
}import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.Banner;
public class MyApplication {
public static void main(String[] args) {
// Simple fluent configuration
new SpringApplicationBuilder(MyApplication.class)
.web(WebApplicationType.SERVLET)
.bannerMode(Banner.Mode.OFF)
.profiles("production")
.properties("server.port=8080")
.run(args);
}
}import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.WebApplicationType;
import org.springframework.context.annotation.Configuration;
public class HierarchicalApplication {
public static void main(String[] args) {
// Create parent-child context hierarchy
new SpringApplicationBuilder()
.sources(ParentConfig.class)
.web(WebApplicationType.NONE) // Parent is non-web
.child(ChildConfig.class) // Child context
.web(WebApplicationType.SERVLET) // Child is web
.run(args);
}
}
@Configuration
class ParentConfig {
// Shared beans available to child
@Bean
public DataSource dataSource() {
return new HikariDataSource();
}
}
@Configuration
class ChildConfig {
// Web-specific beans
// Can inject beans from parent context
@Bean
public WebController webController(DataSource dataSource) {
return new WebController(dataSource);
}
}import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.Banner;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
public class MyApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = new SpringApplicationBuilder()
// Source classes
.sources(MyApplication.class, DatabaseConfig.class)
// Web configuration
.web(WebApplicationType.SERVLET)
// Properties
.properties(
"server.port=8080",
"spring.application.name=my-app",
"logging.level.root=INFO"
)
// Profiles
.profiles("dev", "local")
// Logging and banner
.logStartupInfo(true)
.bannerMode(Banner.Mode.CONSOLE)
// Lifecycle hooks
.registerShutdownHook(true)
// Listeners
.listeners((ApplicationListener<ApplicationReadyEvent>) event -> {
System.out.println("Application ready!");
})
// Run
.run(args);
// Use context as needed
MyService service = context.getBean(MyService.class);
service.doWork();
}
}Strategy interface for creating the ConfigurableApplicationContext used by a SpringApplication. Contexts created by the factory are returned in their default form, with SpringApplication responsible for configuring and refreshing them.
package org.springframework.boot;
import java.util.function.Supplier;
import org.jspecify.annotations.Nullable;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
/**
* Strategy interface for creating ConfigurableApplicationContext instances.
* Created contexts should be returned in their default form, with SpringApplication
* responsible for configuring and refreshing the context.
*
* @since 2.4.0
*/
@FunctionalInterface
public interface ApplicationContextFactory {
/**
* Default factory implementation that creates an appropriate context
* for the WebApplicationType.
*/
ApplicationContextFactory DEFAULT = new DefaultApplicationContextFactory();
/**
* Return the Environment type expected to be set on the created context.
* Used to convert existing environment instances to the correct type.
*
* @param webApplicationType the web application type or null
* @return the expected environment type or null to use default
* @since 2.6.14
*/
default @Nullable Class<? extends ConfigurableEnvironment> getEnvironmentType(
@Nullable WebApplicationType webApplicationType);
/**
* Create a new Environment to be set on the created application context.
* Result must match the type returned by getEnvironmentType().
*
* @param webApplicationType the web application type or null
* @return an environment instance or null to use default
* @since 2.6.14
*/
default @Nullable ConfigurableEnvironment createEnvironment(
@Nullable WebApplicationType webApplicationType);
/**
* Creates the ConfigurableApplicationContext for a SpringApplication,
* respecting the given webApplicationType.
*
* @param webApplicationType the web application type
* @return the newly created application context
*/
@Nullable ConfigurableApplicationContext create(@Nullable WebApplicationType webApplicationType);
/**
* Creates an ApplicationContextFactory that instantiates the given contextClass
* through its primary constructor.
*
* @param contextClass the context class to instantiate
* @return the factory that instantiates the context class
*/
static ApplicationContextFactory ofContextClass(
Class<? extends ConfigurableApplicationContext> contextClass);
/**
* Creates an ApplicationContextFactory that creates contexts by calling
* the given Supplier.
*
* @param supplier the context supplier (e.g., AnnotationConfigApplicationContext::new)
* @return the factory that calls the supplier
*/
static ApplicationContextFactory of(Supplier<ConfigurableApplicationContext> supplier);
}Use the static factory methods to create custom ApplicationContextFactory instances:
import org.springframework.boot.ApplicationContextFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
@SpringBootApplication
public class CustomFactoryApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(CustomFactoryApplication.class);
// Option 1: Use ofContextClass() with a context class
app.setApplicationContextFactory(
ApplicationContextFactory.ofContextClass(GenericApplicationContext.class)
);
// Option 2: Use of() with a Supplier (method reference)
app.setApplicationContextFactory(
ApplicationContextFactory.of(AnnotationConfigApplicationContext::new)
);
// Option 3: Use of() with a lambda
app.setApplicationContextFactory(
ApplicationContextFactory.of(() -> new GenericApplicationContext())
);
app.run(args);
}
}For advanced scenarios, implement the interface directly:
import org.springframework.boot.ApplicationContextFactory;
import org.springframework.boot.WebApplicationType;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
public class MyCustomContextFactory implements ApplicationContextFactory {
@Override
public ConfigurableApplicationContext create(WebApplicationType webApplicationType) {
// Create different context types based on web application type
return switch (webApplicationType) {
case SERVLET -> new AnnotationConfigWebApplicationContext();
case REACTIVE -> new AnnotationConfigReactiveWebApplicationContext();
case NONE -> new AnnotationConfigApplicationContext();
default -> null;
};
}
@Override
public Class<? extends ConfigurableEnvironment> getEnvironmentType(
WebApplicationType webApplicationType) {
// Return appropriate environment type for the context
return switch (webApplicationType) {
case SERVLET -> StandardServletEnvironment.class;
case REACTIVE -> StandardReactiveWebEnvironment.class;
case NONE -> StandardEnvironment.class;
default -> null;
};
}
}@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MyApplication.class);
app.setApplicationContextFactory(new MyCustomContextFactory());
app.run(args);
}
}import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.support.GenericApplicationContext;
new SpringApplicationBuilder(MyApplication.class)
.contextFactory(ApplicationContextFactory.ofContextClass(GenericApplicationContext.class))
.run(args);The DEFAULT factory creates contexts based on WebApplicationType:
AnnotationConfigServletWebServerApplicationContextAnnotationConfigReactiveWebServerApplicationContextAnnotationConfigApplicationContextCustom ApplicationContextFactory implementations are useful for:
ApplicationContextFactory implementations should be stateless and thread-safe, as they may be invoked from multiple threads.
Low-level hook interface for attaching custom SpringApplicationRunListener instances to observe or modify application behavior. Hooks are managed on a per-thread basis, providing isolation when multiple applications run in parallel.
package org.springframework.boot;
import org.jspecify.annotations.Nullable;
/**
* Low-level hook for attaching a SpringApplicationRunListener.
* Managed per-thread for parallel application execution isolation.
*
* @since 3.0.0
*/
@FunctionalInterface
public interface SpringApplicationHook {
/**
* Return the SpringApplicationRunListener to hook into the given SpringApplication.
* @param springApplication the source SpringApplication instance
* @return the SpringApplicationRunListener to attach or null
*/
@Nullable SpringApplicationRunListener getRunListener(SpringApplication springApplication);
}import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationHook;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ConfigurableApplicationContext;
// Example 1: Custom lifecycle listener via hook
public class CustomLifecycleHook implements SpringApplicationHook {
@Override
public SpringApplicationRunListener getRunListener(SpringApplication app) {
return new SpringApplicationRunListener() {
@Override
public void starting(ConfigurableBootstrapContext bootstrapContext) {
System.out.println("Application starting...");
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
System.out.println("Context loaded");
}
@Override
public void started(ConfigurableApplicationContext context, Duration timeTaken) {
System.out.println("Application started in " + timeTaken.toMillis() + "ms");
}
};
}
}
// Example 2: Using withHook for scoped execution
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplicationHook hook = (springApp) -> new CustomRunListener();
// Hook applied only for this execution (thread-local)
ConfigurableApplicationContext context = SpringApplication.withHook(
hook,
() -> SpringApplication.run(MyApplication.class, args)
);
}
}
// Example 3: Testing with hooks
public class ApplicationTest {
@Test
void testApplicationStartup() {
AtomicBoolean started = new AtomicBoolean(false);
SpringApplicationHook testHook = (app) -> new SpringApplicationRunListener() {
@Override
public void started(ConfigurableApplicationContext ctx, Duration time) {
started.set(true);
}
};
SpringApplication.withHook(testHook, () -> {
SpringApplication.run(TestApp.class, new String[0]);
});
assertTrue(started.get());
}
}Thread Isolation: Hooks are stored in ThreadLocal storage, so multiple applications can run concurrently with different hooks without interference.
Listener interface for the SpringApplication run method lifecycle. SpringApplicationRunListener implementations are loaded via SpringFactoriesLoader and must declare a public constructor that accepts a SpringApplication instance and a String[] of arguments.
package org.springframework.boot;
import java.time.Duration;
import org.jspecify.annotations.Nullable;
import org.springframework.boot.bootstrap.ConfigurableBootstrapContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
/**
* Listener for SpringApplication run method. Loaded through SpringFactoriesLoader.
* Implementations should declare a public constructor accepting SpringApplication and String[].
*
* @since 1.0.0
*/
public interface SpringApplicationRunListener {
/**
* Called immediately when the run method has first started.
* Can be used for very early initialization.
*
* @param bootstrapContext the bootstrap context
*/
default void starting(ConfigurableBootstrapContext bootstrapContext);
/**
* Called once the environment has been prepared, but before the
* ApplicationContext has been created.
*
* @param bootstrapContext the bootstrap context
* @param environment the environment
*/
default void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
ConfigurableEnvironment environment);
/**
* Called once the ApplicationContext has been created and prepared,
* but before sources have been loaded.
*
* @param context the application context
*/
default void contextPrepared(ConfigurableApplicationContext context);
/**
* Called once the application context has been loaded but before
* it has been refreshed.
*
* @param context the application context
*/
default void contextLoaded(ConfigurableApplicationContext context);
/**
* Called after the context has been refreshed and the application has started,
* but before CommandLineRunners and ApplicationRunners have been called.
*
* @param context the application context
* @param timeTaken the time taken to start the application or null if unknown
* @since 2.6.0
*/
default void started(ConfigurableApplicationContext context, @Nullable Duration timeTaken);
/**
* Called immediately before the run method finishes, when the application context
* has been refreshed and all CommandLineRunners and ApplicationRunners have been called.
*
* @param context the application context
* @param timeTaken the time taken for the application to be ready or null if unknown
* @since 2.6.0
*/
default void ready(ConfigurableApplicationContext context, @Nullable Duration timeTaken);
/**
* Called when a failure occurs when running the application.
*
* @param context the application context or null if a failure occurred before the context was created
* @param exception the failure
* @since 2.0.0
*/
default void failed(@Nullable ConfigurableApplicationContext context, Throwable exception);
}The listener methods are called in the following order during normal startup:
starting() - Very first callback, before any processing beginsenvironmentPrepared() - After Environment created, before ApplicationContextcontextPrepared() - After ApplicationContext created, before sources loadedcontextLoaded() - After sources loaded, before context refreshstarted() - After context refresh, before runners executeready() - After all runners complete, application fully startedfailed() - Called if any stage fails (instead of later callbacks)To register a custom listener, add it to META-INF/spring.factories:
org.springframework.boot.SpringApplicationRunListener=\
com.example.MyCustomRunListenerpackage com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.boot.bootstrap.ConfigurableBootstrapContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import java.time.Duration;
/**
* Custom run listener for application lifecycle monitoring.
*/
public class MonitoringRunListener implements SpringApplicationRunListener {
private final SpringApplication application;
private final String[] args;
private long startTime;
// Required constructor
public MonitoringRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
}
@Override
public void starting(ConfigurableBootstrapContext bootstrapContext) {
startTime = System.currentTimeMillis();
System.out.println("Application starting...");
}
@Override
public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
ConfigurableEnvironment environment) {
System.out.println("Environment prepared with profiles: " +
String.join(",", environment.getActiveProfiles()));
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
System.out.println("Context prepared: " + context.getId());
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
System.out.println("Context loaded with " +
context.getBeanDefinitionCount() + " beans");
}
@Override
public void started(ConfigurableApplicationContext context, Duration timeTaken) {
System.out.println("Application started in " + timeTaken.toMillis() + "ms");
}
@Override
public void ready(ConfigurableApplicationContext context, Duration timeTaken) {
System.out.println("Application ready to serve requests");
long totalTime = System.currentTimeMillis() - startTime;
System.out.println("Total startup time: " + totalTime + "ms");
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
System.err.println("Application failed to start: " + exception.getMessage());
}
}| Feature | SpringApplicationRunListener | SpringApplicationHook |
|---|---|---|
| Registration | spring.factories | Programmatic (ThreadLocal) |
| Lifecycle coverage | All 7 lifecycle stages | Via custom RunListener |
| Use case | Framework extensions, monitoring | Per-thread customization, testing |
| Scope | Application-wide | Thread-local |
| Constructor | Requires SpringApplication + args | Not instantiated |
When to use:
SpringApplicationRunListener for application-wide lifecycle monitoring and framework integrationSpringApplicationHook for thread-local customization (especially in tests)Fluent API for configuring and running an augmented SpringApplication where additional configuration classes or profiles should be applied to an existing main method.
package org.springframework.boot;
/**
* Used to configure and run an augmented SpringApplication with additional configuration.
* Created via SpringApplication.from(main).
*
* @since 3.1.0
*/
public static class SpringApplication.Augmented {
/**
* Return a new Augmented instance with additional sources.
* @param sources configuration classes to apply when application runs
* @return new Augmented instance with merged sources
*/
public Augmented with(Class<?>... sources);
/**
* Return a new Augmented instance with additional profiles.
* @param profiles the profiles to activate when application runs
* @return new Augmented instance with merged profiles
* @since 3.4.0
*/
public Augmented withAdditionalProfiles(String... profiles);
/**
* Run the application using the given args.
* @param args the main method arguments
* @return Running instance providing access to the ApplicationContext
*/
public SpringApplication.Running run(String... args);
}import org.springframework.boot.SpringApplication;
// Example 1: Augment existing application with test configuration
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
// Test augmenting main application with additional config
public class IntegrationTest {
public static void main(String[] args) {
SpringApplication.Running running = SpringApplication
.from(MyApplication::main)
.with(TestConfiguration.class, MockDataConfiguration.class)
.run(args);
ConfigurableApplicationContext context = running.getApplicationContext();
// Use augmented context for testing
MyService service = context.getBean(MyService.class);
service.performTestOperations();
}
}
// Example 2: Augment with additional profiles
public class ProfileAugmentationExample {
public static void main(String[] args) {
SpringApplication.Running app = SpringApplication
.from(MyApplication::main)
.withAdditionalProfiles("debug", "local-db")
.run(new String[]{"--spring.application.name=augmented-app"});
ConfigurableApplicationContext context = app.getApplicationContext();
String[] profiles = context.getEnvironment().getActiveProfiles();
System.out.println("Active profiles: " + Arrays.toString(profiles));
}
}
// Example 3: Chain multiple augmentations
public class ChainedAugmentationExample {
public static void main(String[] args) {
SpringApplication.Running running = SpringApplication
.from(MyApplication::main)
.with(SecurityConfiguration.class)
.with(DatabaseConfiguration.class)
.withAdditionalProfiles("test", "h2")
.run(args);
ConfigurableApplicationContext ctx = running.getApplicationContext();
// Application running with all augmentations applied
}
}
// Example 4: Augmentation for containerized testing
@SpringBootApplication
public class ContainerizedApp {
public static void main(String[] args) {
SpringApplication.run(ContainerizedApp.class, args);
}
}
public class DockerComposeTest {
@Test
void testWithDockerCompose() {
// Augment application with container-specific configuration
SpringApplication.Running running = SpringApplication
.from(ContainerizedApp::main)
.with(TestcontainersConfiguration.class)
.withAdditionalProfiles("testcontainers")
.run(new String[0]);
ConfigurableApplicationContext context = running.getApplicationContext();
// Perform tests against augmented application
DataSource dataSource = context.getBean(DataSource.class);
assertNotNull(dataSource);
context.close();
}
}Use Cases:
Interface representing a running Spring Boot application, providing access to the application context created by an augmented application run.
package org.springframework.boot;
import org.springframework.context.ConfigurableApplicationContext;
/**
* Provides access to details of a SpringApplication run using Augmented.run().
*
* @since 3.1.0
*/
public interface SpringApplication.Running {
/**
* Return the root ConfigurableApplicationContext of the running application.
* @return the root application context
* @throws IllegalStateException if no root context or multiple root contexts found
*/
ConfigurableApplicationContext getApplicationContext();
}import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
// Example 1: Access beans from running application
public class ApplicationAccess {
public static void main(String[] args) {
SpringApplication.Running running = SpringApplication
.from(MyApplication::main)
.with(AdditionalConfig.class)
.run(args);
// Get the application context
ConfigurableApplicationContext context = running.getApplicationContext();
// Access beans
MyService service = context.getBean(MyService.class);
service.execute();
// Access environment
String appName = context.getEnvironment().getProperty("spring.application.name");
System.out.println("Running application: " + appName);
// Gracefully shutdown when done
context.close();
}
}
// Example 2: Testing pattern with Running
public class ApplicationRunner {
private SpringApplication.Running running;
public void start(String[] args) {
this.running = SpringApplication
.from(MyApplication::main)
.with(TestConfiguration.class)
.run(args);
}
public <T> T getBean(Class<T> beanClass) {
return running.getApplicationContext().getBean(beanClass);
}
public void stop() {
if (running != null) {
running.getApplicationContext().close();
}
}
}
// Example 3: Multiple application contexts (parent-child)
public class MultiContextExample {
public static void main(String[] args) {
// When using parent-child contexts, Running returns the root context
SpringApplication.Running running = SpringApplication
.from(MyApplication::main)
.run(args);
ConfigurableApplicationContext rootContext = running.getApplicationContext();
// Root context has no parent
assert rootContext.getParent() == null;
// Child contexts are accessible via root
String[] beanNames = rootContext.getBeanDefinitionNames();
System.out.println("Beans in root context: " + beanNames.length);
}
}Behavior:
ApplicationContext when the application has parent-child context hierarchiesIllegalStateException if no root context or multiple root contexts existcontext.close()Exception thrown when a SpringApplication run is abandoned before the context is fully prepared. This typically occurs when a SpringApplicationHook terminates the application early or when startup is cancelled programmatically.
package org.springframework.boot;
import org.springframework.context.ConfigurableApplicationContext;
import org.jspecify.annotations.Nullable;
/**
* Exception thrown when a run is abandoned before the context is prepared.
* Used internally to signal early termination of application startup.
*
* Thread Safety: Thread-safe (immutable after construction).
*
* @since 3.0.0
*/
public static class AbandonedRunException extends RuntimeException {
/**
* Creates a new AbandonedRunException with no associated context.
*/
public AbandonedRunException() {
this(null);
}
/**
* Creates a new AbandonedRunException with the specified application context.
* The context may represent a partially initialized application.
*
* @param applicationContext the application context that was being prepared (may be null)
*/
public AbandonedRunException(@Nullable ConfigurableApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
/**
* Returns the application context if one was created before the run was abandoned.
*
* @return the application context, or null if no context was created
*/
@Nullable
public ConfigurableApplicationContext getApplicationContext() {
return applicationContext;
}
private final ConfigurableApplicationContext applicationContext;
}Example 1: Hook-Based Early Termination
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationHook;
import org.springframework.boot.SpringApplication.AbandonedRunException;
public class ConditionalStartupExample {
public static void main(String[] args) {
// Hook that abandons startup based on environment check
SpringApplicationHook validationHook = new SpringApplicationHook() {
@Override
public SpringApplicationRunListener create(SpringApplication application) {
return new SpringApplicationRunListener() {
@Override
public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
ConfigurableEnvironment environment) {
// Check critical environment variable
String requiredValue = environment.getProperty("REQUIRED_CONFIG");
if (requiredValue == null) {
System.err.println("REQUIRED_CONFIG not set. Abandoning startup.");
// Abandon startup - throws AbandonedRunException internally
throw new IllegalStateException("Missing required configuration");
}
}
};
}
};
try {
SpringApplication.withHook(validationHook, () -> {
SpringApplication.run(MyApplication.class, args);
});
} catch (AbandonedRunException e) {
System.err.println("Application startup was abandoned");
ConfigurableApplicationContext context = e.getApplicationContext();
if (context != null) {
System.out.println("Partial context was created: " + context);
// Cleanup if needed
context.close();
}
System.exit(1);
}
}
}Example 2: Handling Abandoned Runs
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplication.AbandonedRunException;
import org.springframework.context.ConfigurableApplicationContext;
public class RobustStartupExample {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MyApplication.class);
try {
ConfigurableApplicationContext context = app.run(args);
System.out.println("Application started successfully");
registerShutdownHook(context);
} catch (AbandonedRunException e) {
// Startup was abandoned by hook or internal logic
System.err.println("Application startup abandoned: " + e.getMessage());
// Check if partial context exists
ConfigurableApplicationContext partialContext = e.getApplicationContext();
if (partialContext != null) {
System.out.println("Cleaning up partial context...");
try {
partialContext.close();
} catch (Exception closeEx) {
System.err.println("Error closing partial context: " + closeEx.getMessage());
}
}
// Exit with error code
System.exit(10);
} catch (Exception e) {
// Other startup failures
System.err.println("Application failed to start: " + e.getMessage());
e.printStackTrace();
System.exit(1);
}
}
private static void registerShutdownHook(ConfigurableApplicationContext context) {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("Shutdown hook triggered");
context.close();
}));
}
}Example 3: Testing Early Termination
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationHook;
import org.springframework.boot.SpringApplication.AbandonedRunException;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.*;
public class AbandonedRunTest {
@Test
void testAbandonedRun() {
SpringApplicationHook hook = application -> {
return new SpringApplicationRunListener() {
@Override
public void starting(ConfigurableBootstrapContext bootstrapContext) {
// Simulate condition that abandons startup
throw new RuntimeException("Simulated abandonment");
}
};
};
assertThatThrownBy(() -> {
SpringApplication.withHook(hook, () -> {
SpringApplication.run(TestApplication.class);
});
}).isInstanceOfAny(AbandonedRunException.class, RuntimeException.class);
}
@Test
void testAbandonedRunWithContext() {
SpringApplication app = new SpringApplication(TestApplication.class);
try {
app.run();
fail("Expected AbandonedRunException");
} catch (AbandonedRunException e) {
// Verify exception details
assertThat(e.getMessage()).contains("abandoned");
// May have partial context
ConfigurableApplicationContext context = e.getApplicationContext();
if (context != null) {
assertThat(context).isNotNull();
context.close();
}
}
}
}When AbandonedRunException is Thrown:
SpringApplicationHook throws exception during early lifecycle phasesHandling Strategy:
AbandonedRunException separately from general exceptionsgetApplicationContext()Thread Safety: Exception is immutable after construction and safe to pass between threads.
Enum that determines the type of web application to create.
package org.springframework.boot;
/**
* Enum that determines the type of web application.
*/
public enum WebApplicationType {
/**
* Application should not run as web application.
* No embedded web server will be started.
* Use for: CLI applications, batch jobs, scheduled tasks.
*/
NONE,
/**
* Application should run as servlet-based web application.
* Starts an embedded servlet web server (Tomcat, Jetty, Undertow).
* Use for: Traditional Spring MVC web applications, REST APIs.
* Requires: spring-boot-starter-web or spring-boot-starter-tomcat
*/
SERVLET,
/**
* Application should run as reactive web application.
* Starts an embedded reactive web server (Netty, Tomcat, Jetty, Undertow).
* Use for: Reactive Spring WebFlux applications, reactive REST APIs.
* Requires: spring-boot-starter-webflux
*/
REACTIVE;
/**
* Deduce web application type from classpath.
* Checks for presence of web-related classes.
*/
static WebApplicationType deduceFromClasspath();
}Spring Boot automatically detects the web application type based on classpath:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
// Example 1: Non-web application
public class BatchApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(BatchApplication.class);
app.setWebApplicationType(WebApplicationType.NONE);
app.run(args);
}
}
// Example 2: Reactive application
public class ReactiveApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(ReactiveApplication.class);
app.setWebApplicationType(WebApplicationType.REACTIVE);
app.run(args);
}
}
// Example 3: Traditional servlet application (default if spring-web is present)
public class ServletApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(ServletApplication.class);
// Explicitly set (though auto-detected if spring-web is present)
app.setWebApplicationType(WebApplicationType.SERVLET);
app.run(args);
}
}Callback interface that receives parsed application arguments.
package org.springframework.boot;
import org.springframework.boot.ApplicationArguments;
/**
* Interface for beans that should run when contained within a SpringApplication.
* Receives ApplicationArguments with parsed command-line options.
* Extends Runner marker interface for ordering support.
* Note: Runner is a package-private internal interface, not part of the public API.
*/
@FunctionalInterface
public interface ApplicationRunner extends Runner {
/**
* Callback used to run the bean.
* Called after context has been refreshed and all CommandLineRunner beans called.
*
* @param args application arguments with parsed options
* @throws Exception on error
*/
void run(ApplicationArguments args) throws Exception;
}import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.ApplicationArguments;
import org.springframework.stereotype.Component;
import org.springframework.core.annotation.Order;
@Component
@Order(1) // Lower values have higher priority
public class DataLoader implements ApplicationRunner {
private final DataRepository repository;
public DataLoader(DataRepository repository) {
this.repository = repository;
}
@Override
public void run(ApplicationArguments args) throws Exception {
// Check for --import option
if (args.containsOption("import")) {
List<String> files = args.getOptionValues("import");
for (String file : files) {
System.out.println("Importing data from: " + file);
repository.importFrom(file);
}
}
// Check for --init-db flag
if (args.containsOption("init-db")) {
System.out.println("Initializing database...");
repository.initialize();
}
// Access non-option arguments
List<String> nonOptionArgs = args.getNonOptionArgs();
System.out.println("Non-option args: " + nonOptionArgs);
// Get all source arguments
String[] sourceArgs = args.getSourceArgs();
System.out.println("Total arguments: " + sourceArgs.length);
}
}
// Run with: java -jar app.jar --import=data.csv --init-db file1 file2
// Output:
// Importing data from: data.csv
// Initializing database...
// Non-option args: [file1, file2]
// Total arguments: 4Callback interface that receives raw string arguments.
package org.springframework.boot;
/**
* Interface for beans that should run when contained within a SpringApplication.
* Receives raw String arguments.
* Extends Runner marker interface for ordering support.
* Note: Runner is a package-private internal interface, not part of the public API.
*/
@FunctionalInterface
public interface CommandLineRunner extends Runner {
/**
* Callback used to run the bean.
* Called after context has been refreshed.
*
* @param args raw command-line arguments
* @throws Exception on error
*/
void run(String... args) throws Exception;
}import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import org.springframework.core.annotation.Order;
@Component
@Order(2) // Runs after Order(1) runners
public class StartupLogger implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("Application started with " + args.length + " arguments");
// Log each argument
for (int i = 0; i < args.length; i++) {
System.out.println("Arg " + i + ": " + args[i]);
}
// Perform startup checks
performHealthCheck();
logSystemInfo();
}
private void performHealthCheck() {
System.out.println("Running health check...");
// Health check logic
}
private void logSystemInfo() {
System.out.println("Java version: " + System.getProperty("java.version"));
System.out.println("OS: " + System.getProperty("os.name"));
System.out.println("Available processors: " +
Runtime.getRuntime().availableProcessors());
}
}Both CommandLineRunner and ApplicationRunner beans are executed together in a single pass, sorted by their @Order annotation or Ordered implementation. The type of runner does NOT determine execution order - only the @Order value matters.
Runners are retrieved together via a common Runner marker interface and sorted into a single list before execution. A CommandLineRunner with @Order(2) will execute AFTER an ApplicationRunner with @Order(1).
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import org.springframework.core.annotation.Order;
@Component
@Order(1)
class FirstCommandLineRunner implements CommandLineRunner {
public void run(String... args) {
System.out.println("1. First CommandLineRunner (@Order(1))");
}
}
@Component
@Order(2)
class FirstApplicationRunner implements ApplicationRunner {
public void run(ApplicationArguments args) {
System.out.println("2. First ApplicationRunner (@Order(2))");
}
}
@Component
@Order(3)
class SecondCommandLineRunner implements CommandLineRunner {
public void run(String... args) {
System.out.println("3. Second CommandLineRunner (@Order(3))");
}
}
@Component
@Order(4)
class SecondApplicationRunner implements ApplicationRunner {
public void run(ApplicationArguments args) {
System.out.println("4. Second ApplicationRunner (@Order(4))");
}
}
// Output shows interleaved execution based on @Order value:
// 1. First CommandLineRunner (@Order(1))
// 2. First ApplicationRunner (@Order(2))
// 3. Second CommandLineRunner (@Order(3))
// 4. Second ApplicationRunner (@Order(4))Provides access to the arguments that were used to run a SpringApplication, with support for both option arguments (like --foo=bar) and non-option arguments.
package org.springframework.boot;
import java.util.List;
import java.util.Set;
import org.jspecify.annotations.Nullable;
/**
* Provides access to the arguments that were used to run a SpringApplication.
*
* Thread Safety: Implementations are immutable and thread-safe after creation.
*
* @since 1.3.0
*/
public interface ApplicationArguments {
/**
* Return the raw unprocessed arguments that were passed to the application.
*
* @return the arguments (never null)
*/
String[] getSourceArgs();
/**
* Return the names of all option arguments. For example, if the arguments
* were "--foo=bar --debug" this would return the values ["foo", "debug"].
*
* @return the option names or an empty set (never null)
*/
Set<String> getOptionNames();
/**
* Return whether the set of option arguments parsed from the arguments
* contains an option with the given name.
*
* @param name the name to check
* @return true if the arguments contain an option with the given name
*/
boolean containsOption(String name);
/**
* Return the collection of values associated with the arguments option
* having the given name.
*
* Behavior:
* - If the option is present and has no argument (e.g. "--foo"), returns
* an empty collection ([])
* - If the option is present and has a single value (e.g. "--foo=bar"),
* returns a collection having one element (["bar"])
* - If the option is present and has multiple values (e.g. "--foo=bar
* --foo=baz"), returns a collection having elements for each value
* (["bar", "baz"])
* - If the option is not present, returns null
*
* @param name the name of the option
* @return a list of option values for the given name, or null if not present
*/
@Nullable List<String> getOptionValues(String name);
/**
* Return the collection of non-option arguments parsed.
* Non-option arguments are those that don't start with "--".
*
* @return the non-option arguments or an empty list (never null)
*/
List<String> getNonOptionArgs();
}Key Features:
-- (e.g., --foo=bar, --debug)-- prefixgetOptionValues() can return null (when option not present)import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.ApplicationArguments;
import org.springframework.stereotype.Component;
@Component
public class ArgumentsProcessor implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
// Get raw arguments
String[] source = args.getSourceArgs();
System.out.println("Raw arguments: " + Arrays.toString(source));
// Check for specific options
if (args.containsOption("debug")) {
System.out.println("Debug mode enabled");
enableDebugMode();
}
if (args.containsOption("verbose")) {
System.out.println("Verbose mode enabled");
enableVerboseMode();
}
// Get option values
if (args.containsOption("config")) {
List<String> configs = args.getOptionValues("config");
System.out.println("Config files: " + configs);
for (String config : configs) {
loadConfiguration(config);
}
}
if (args.containsOption("port")) {
List<String> ports = args.getOptionValues("port");
if (!ports.isEmpty()) {
int port = Integer.parseInt(ports.get(0));
System.out.println("Using port: " + port);
setPort(port);
}
}
// Get all option names
Set<String> optionNames = args.getOptionNames();
System.out.println("All options: " + optionNames);
// Get non-option arguments
List<String> nonOptionArgs = args.getNonOptionArgs();
System.out.println("Non-option arguments: " + nonOptionArgs);
// Process files specified as non-option args
for (String file : nonOptionArgs) {
System.out.println("Processing file: " + file);
processFile(file);
}
}
private void enableDebugMode() { /* implementation */ }
private void enableVerboseMode() { /* implementation */ }
private void loadConfiguration(String config) { /* implementation */ }
private void setPort(int port) { /* implementation */ }
private void processFile(String file) { /* implementation */ }
}
// Example command line:
// java -jar app.jar --debug --config=app.properties --config=db.properties --port=8080 file1.txt file2.txt
//
// Output:
// Raw arguments: [--debug, --config=app.properties, --config=db.properties, --port=8080, file1.txt, file2.txt]
// Debug mode enabled
// Config files: [app.properties, db.properties]
// Using port: 8080
// All options: [debug, config, port]
// Non-option arguments: [file1.txt, file2.txt]
// Processing file: file1.txt
// Processing file: file2.txtThis example demonstrates the specific behavior of getOptionValues() including null returns and multi-value handling.
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.ApplicationArguments;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class AdvancedArgumentsHandler implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
// Example 1: Option not present - returns null
List<String> missingOption = args.getOptionValues("notpresent");
if (missingOption == null) {
System.out.println("Option 'notpresent' was not provided (null)");
}
// Example 2: Option present without value - returns empty list
if (args.containsOption("flag")) {
List<String> flagValues = args.getOptionValues("flag");
System.out.println("Flag option values: " + flagValues);
// Output: Flag option values: []
System.out.println("Is empty: " + flagValues.isEmpty());
// Output: Is empty: true
}
// Example 3: Option with single value
if (args.containsOption("name")) {
List<String> names = args.getOptionValues("name");
String name = names.get(0);
System.out.println("Name: " + name);
}
// Example 4: Option with multiple values
if (args.containsOption("include")) {
List<String> includes = args.getOptionValues("include");
System.out.println("Include count: " + includes.size());
for (int i = 0; i < includes.size(); i++) {
System.out.println(" Include[" + i + "]: " + includes.get(i));
}
}
// Best practice: Check for null before using
List<String> servers = args.getOptionValues("server");
if (servers != null) {
servers.forEach(server -> connectToServer(server));
} else {
System.out.println("No servers specified, using defaults");
connectToDefaultServer();
}
// Alternative: Use containsOption first
if (args.containsOption("timeout")) {
List<String> timeouts = args.getOptionValues("timeout");
// Safe - we know it's not null because containsOption returned true
int timeout = Integer.parseInt(timeouts.get(0));
setTimeout(timeout);
}
}
private void connectToServer(String server) { /* implementation */ }
private void connectToDefaultServer() { /* implementation */ }
private void setTimeout(int timeout) { /* implementation */ }
}
// Example command line 1: Option without value
// java -jar app.jar --flag --name=John
//
// Output:
// Option 'notpresent' was not provided (null)
// Flag option values: []
// Is empty: true
// Name: John
// Example command line 2: Multiple values for same option
// java -jar app.jar --include=*.java --include=*.xml --include=*.properties
//
// Output:
// Include count: 3
// Include[0]: *.java
// Include[1]: *.xml
// Include[2]: *.properties
// Example command line 3: No server option
// java -jar app.jar --name=Test
//
// Output:
// No servers specified, using defaultsImportant Notes:
getOptionValues() returns null only when the option is not present at all[] when option is present but has no value (e.g., --flag)--name=John)--include=a --include=b)containsOption() first to avoid NullPointerExceptionThe default implementation of ApplicationArguments interface used by Spring Boot.
package org.springframework.boot;
import java.util.List;
import java.util.Set;
import org.jspecify.annotations.Nullable;
/**
* Default implementation of {@link ApplicationArguments}.
*
* Thread Safety: Immutable and thread-safe after creation.
*
* @since 1.4.1
*/
public class DefaultApplicationArguments implements ApplicationArguments {
/**
* Create a new DefaultApplicationArguments instance.
*
* @param args the source arguments (must not be null)
* @throws IllegalArgumentException if args is null
*/
public DefaultApplicationArguments(String... args);
@Override
public String[] getSourceArgs();
@Override
public Set<String> getOptionNames();
@Override
public boolean containsOption(String name);
@Override
public @Nullable List<String> getOptionValues(String name);
@Override
public List<String> getNonOptionArgs();
}Key Characteristics:
ApplicationArgumentsgetOptionValues() can return nullUsage Pattern:
import org.springframework.boot.DefaultApplicationArguments;
import org.springframework.boot.ApplicationArguments;
// Typically injected by Spring Boot automatically
@Component
public class MyComponent {
private final ApplicationArguments args;
// Spring Boot provides DefaultApplicationArguments instance
public MyComponent(ApplicationArguments args) {
this.args = args; // This is a DefaultApplicationArguments
}
}
// Manual instantiation (rare - usually let Spring Boot handle this)
String[] commandLineArgs = {"--debug", "--port=8080", "file.txt"};
ApplicationArguments args = new DefaultApplicationArguments(commandLineArgs);
// Use as any ApplicationArguments
if (args.containsOption("debug")) {
System.out.println("Debug mode enabled");
}When to Use:
ApplicationArgumentsImplementation Note:
Internally uses Spring's SimpleCommandLinePropertySource for parsing command-line arguments according to standard conventions (GNU-style with -- prefix for options).
Spring Boot displays a startup banner when an application starts. You can customize or disable the banner using the Banner interface and related classes.
Functional interface for writing custom banners programmatically.
package org.springframework.boot;
import java.io.PrintStream;
import org.jspecify.annotations.Nullable;
import org.springframework.core.env.Environment;
/**
* Interface for writing a banner programmatically.
*/
@FunctionalInterface
public interface Banner {
/**
* Print the banner to the specified print stream.
*
* @param environment the Spring environment
* @param sourceClass the source class for the application or null
* @param out the output print stream
*/
void printBanner(Environment environment, @Nullable Class<?> sourceClass, PrintStream out);
/**
* Banner display mode enumeration.
*/
enum Mode {
/** Disable printing of the banner */
OFF,
/** Print the banner to System.out */
CONSOLE,
/** Print the banner to the log file */
LOG
}
}Banner implementation that prints from a source text resource file.
package org.springframework.boot;
import org.springframework.core.io.Resource;
/**
* Banner implementation that prints from a Resource.
* Supports variable substitution from the Environment.
*/
public class ResourceBanner implements Banner {
/**
* Create a new ResourceBanner instance.
*
* @param resource the resource to load (must exist)
*/
public ResourceBanner(Resource resource);
/**
* Print the banner by reading from the resource.
*
* @param environment the Spring environment
* @param sourceClass the source class for the application or null
* @param out the output print stream
*/
@Override
public void printBanner(Environment environment, @Nullable Class<?> sourceClass, PrintStream out);
}import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.env.Environment;
import java.io.PrintStream;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MyApplication.class);
// Set custom banner
app.setBanner(new Banner() {
@Override
public void printBanner(Environment environment, Class<?> sourceClass, PrintStream out) {
out.println("=================================");
out.println(" My Custom Application Banner");
out.println(" Version: " + environment.getProperty("app.version", "1.0.0"));
out.println(" Profile: " + String.join(",", environment.getActiveProfiles()));
out.println("=================================");
}
});
app.run(args);
}
}Create a banner text file in src/main/resources/banner.txt:
__ __ _
| \/ |_ _ / \ _ __ _ __
| |\/| | | | | / _ \ | '_ \| '_ \
| | | | |_| |/ ___ \| |_) | |_) |
|_| |_|\__, /_/ \_\ .__/| .__/
|___/ |_| |_|
Application: ${spring.application.name}
Version: ${application.version:unknown}
Spring Boot: ${spring-boot.version}import org.springframework.boot.ResourceBanner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.io.ClassPathResource;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MyApplication.class);
// Load banner from resource file
app.setBanner(new ResourceBanner(new ClassPathResource("banner.txt")));
app.run(args);
}
}import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MyApplication.class);
// Disable banner completely
app.setBannerMode(Banner.Mode.OFF);
// Or print to log instead of console
// app.setBannerMode(Banner.Mode.LOG);
app.run(args);
}
}You can also control the banner mode via configuration:
# application.properties
spring.main.banner-mode=off
# Options: off, console, logimport org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MyApplication.class);
// Lambda banner (Banner is a functional interface)
app.setBanner((environment, sourceClass, out) -> {
out.println("╔════════════════════════════════╗");
out.println("║ Application Starting... ║");
out.println("╚════════════════════════════════╝");
});
app.run(args);
}
}When using ResourceBanner with a text file, the following variables are available:
| Variable | Description | Example |
|---|---|---|
${spring.application.name} | Application name | my-app |
${application.version} | Application version | 1.0.0 |
${spring-boot.version} | Spring Boot version | 4.0.0 |
${application.formatted-version} | Formatted version | (v1.0.0) |
${spring-boot.formatted-version} | Formatted Boot version | (v4.0.0) |
${AnsiColor.NAME} | ANSI color codes | ${AnsiColor.BRIGHT_BLUE} |
${AnsiBackground.NAME} | ANSI background | ${AnsiBackground.GREEN} |
${AnsiStyle.NAME} | ANSI styles | ${AnsiStyle.BOLD} |
Example with colors:
${AnsiColor.BRIGHT_CYAN}
__ __ _
| \/ |_ _ / \ _ __ _ __
| |\/| | | | | / _ \ | '_ \| '_ \
| | | | |_| |/ ___ \| |_) | |_) |
|_| |_|\__, /_/ \_\ .__/| .__/
|___/ |_| |_|
${AnsiColor.DEFAULT}
Application: ${AnsiStyle.BOLD}${spring.application.name}${AnsiStyle.NORMAL}
Version: ${application.version:1.0.0}Interface for generating exit codes when application terminates.
package org.springframework.boot;
/**
* Interface used to generate exit codes when application terminates.
* Implementations should be registered as beans in the application context.
*/
@FunctionalInterface
public interface ExitCodeGenerator {
/**
* Returns the exit code that should be returned.
* Called when SpringApplication.exit() is invoked.
*
* @return the exit code (typically 0 for success, non-zero for error)
*/
int getExitCode();
}Event fired when an application exit code has been determined from an ExitCodeGenerator.
package org.springframework.boot;
import org.springframework.context.ApplicationEvent;
/**
* Event fired when an application exit code has been determined from an
* ExitCodeGenerator.
*
* @since 1.3.2
*/
public class ExitCodeEvent extends ApplicationEvent {
/**
* Create a new ExitCodeEvent instance.
* @param source the source of the event
* @param exitCode the exit code
*/
public ExitCodeEvent(Object source, int exitCode);
/**
* Return the exit code that will be used to exit the JVM.
* @return the exit code
*/
public int getExitCode();
}import org.springframework.boot.ExitCodeGenerator;
import org.springframework.stereotype.Component;
@Component
public class ApplicationExitCodeGenerator implements ExitCodeGenerator {
private int exitCode = 0;
public void setExitCode(int exitCode) {
this.exitCode = exitCode;
}
@Override
public int getExitCode() {
return this.exitCode;
}
}
// Usage in main method
public class MyApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context =
SpringApplication.run(MyApplication.class, args);
// Get the exit code generator bean
ApplicationExitCodeGenerator generator =
context.getBean(ApplicationExitCodeGenerator.class);
// Exit with generated code
int exitCode = SpringApplication.exit(context);
System.exit(exitCode);
}
}import org.springframework.boot.ExitCodeExceptionMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ExitCodeConfig {
@Bean
public ExitCodeExceptionMapper exitCodeExceptionMapper() {
return exception -> {
if (exception instanceof IllegalArgumentException) {
return 1; // Invalid argument
}
else if (exception instanceof IllegalStateException) {
return 2; // Invalid state
}
else if (exception instanceof IOException) {
return 3; // I/O error
}
else {
return 99; // Unknown error
}
};
}
}import org.springframework.boot.ExitCodeEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class ExitCodeListener {
@EventListener
public void onExitCode(ExitCodeEvent event) {
int exitCode = event.getExitCode();
System.out.println("Application exiting with code: " + exitCode);
// Perform cleanup based on exit code
if (exitCode != 0) {
System.err.println("Application failed, performing error cleanup");
performErrorCleanup();
} else {
System.out.println("Application succeeded, performing normal cleanup");
performNormalCleanup();
}
}
private void performErrorCleanup() { /* implementation */ }
private void performNormalCleanup() { /* implementation */ }
}SPI (Service Provider Interface) for custom reporting of SpringApplication startup errors. Implementations are loaded via spring.factories and must provide a constructor accepting a single ConfigurableApplicationContext parameter.
package org.springframework.boot;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.io.support.SpringFactoriesLoader;
/**
* Callback interface used to support custom reporting of SpringApplication
* startup errors. Reporters are loaded through SpringFactoriesLoader and must
* declare a public constructor with a single ConfigurableApplicationContext parameter.
*
* @since 2.0.0
*/
@FunctionalInterface
public interface SpringBootExceptionReporter {
/**
* Report a startup failure to the user.
*
* @param failure the source failure
* @return true if the failure was reported, false if default reporting should occur
*/
boolean reportException(Throwable failure);
}SpringFactoriesLoaderConfigurableApplicationContexttrue to suppress default error reporting, false to allow itpackage com.example.reporting;
import org.springframework.boot.SpringBootExceptionReporter;
import org.springframework.context.ConfigurableApplicationContext;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.time.Instant;
/**
* Custom exception reporter that logs failures to a monitoring system.
*/
public class CustomExceptionReporter implements SpringBootExceptionReporter {
private final ConfigurableApplicationContext context;
// Required constructor with ConfigurableApplicationContext parameter
public CustomExceptionReporter(ConfigurableApplicationContext context) {
this.context = context;
}
@Override
public boolean reportException(Throwable failure) {
// Log to monitoring system
logToMonitoring(failure);
// Log detailed information
System.err.println("=".repeat(80));
System.err.println("APPLICATION STARTUP FAILURE");
System.err.println("=".repeat(80));
System.err.println("Timestamp: " + Instant.now());
System.err.println("Application: " + getApplicationName());
System.err.println("Exception: " + failure.getClass().getName());
System.err.println("Message: " + failure.getMessage());
System.err.println("\nStack Trace:");
System.err.println(getStackTraceAsString(failure));
System.err.println("=".repeat(80));
// Return false to allow default Spring Boot error reporting as well
return false;
}
private String getApplicationName() {
if (context.getEnvironment().containsProperty("spring.application.name")) {
return context.getEnvironment().getProperty("spring.application.name");
}
return "Unknown Application";
}
private String getStackTraceAsString(Throwable throwable) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
throwable.printStackTrace(pw);
return sw.toString();
}
private void logToMonitoring(Throwable failure) {
// Send to external monitoring system (e.g., Sentry, New Relic, Datadog)
// MonitoringClient.reportError(failure);
}
}Create or update src/main/resources/META-INF/spring.factories:
# SpringBootExceptionReporter implementations
org.springframework.boot.SpringBootExceptionReporter=\
com.example.reporting.CustomExceptionReporterpackage com.example.reporting;
import org.springframework.boot.SpringBootExceptionReporter;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;
/**
* Exception reporter that only reports in production environments
* and provides different handling based on exception type.
*/
public class ProductionExceptionReporter implements SpringBootExceptionReporter {
private final ConfigurableApplicationContext context;
private final Environment environment;
public ProductionExceptionReporter(ConfigurableApplicationContext context) {
this.context = context;
this.environment = context.getEnvironment();
}
@Override
public boolean reportException(Throwable failure) {
// Only report in production
String[] activeProfiles = environment.getActiveProfiles();
boolean isProduction = Arrays.asList(activeProfiles).contains("production");
if (!isProduction) {
// In non-production, use default reporting
return false;
}
// Handle different exception types
if (failure instanceof org.springframework.beans.factory.BeanCreationException) {
reportBeanCreationFailure((BeanCreationException) failure);
} else if (failure instanceof org.springframework.boot.web.server.WebServerException) {
reportWebServerFailure((WebServerException) failure);
} else {
reportGenericFailure(failure);
}
// Send alert to operations team
sendAlertToOps(failure);
// Return true to suppress default console output in production
return true;
}
private void reportBeanCreationFailure(BeanCreationException ex) {
System.err.println("BEAN CREATION FAILURE: " + ex.getBeanName());
// Log to centralized logging system
}
private void reportWebServerFailure(WebServerException ex) {
System.err.println("WEB SERVER FAILURE: " + ex.getMessage());
// Log to centralized logging system
}
private void reportGenericFailure(Throwable ex) {
System.err.println("STARTUP FAILURE: " + ex.getMessage());
// Log to centralized logging system
}
private void sendAlertToOps(Throwable failure) {
// Send PagerDuty/Slack/email alert
// AlertService.sendCriticalAlert("Application startup failed", failure);
}
}You can register multiple exception reporters:
# spring.factories
org.springframework.boot.SpringBootExceptionReporter=\
com.example.reporting.MonitoringReporter,\
com.example.reporting.SlackNotificationReporter,\
com.example.reporting.FileLogReporterAll registered reporters will be invoked in the order they are listed. Each reporter's return value is evaluated independently:
true, default reporting may still occur if other reporters return falseSpringBootExceptionReporter works alongside FailureAnalyzer implementations:
falseThis allows you to:
FailureAnalyzer implementations for diagnosticsSpringBootExceptionReporter for reporting to external systems// Example combining both
public class IntegratedExceptionReporter implements SpringBootExceptionReporter {
private final ConfigurableApplicationContext context;
public IntegratedExceptionReporter(ConfigurableApplicationContext context) {
this.context = context;
}
@Override
public boolean reportException(Throwable failure) {
// Check if failure analysis is available
FailureAnalysis analysis = analyzeFailure(failure);
if (analysis != null) {
// Report with analysis details
reportWithAnalysis(failure, analysis);
} else {
// Report without analysis
reportWithoutAnalysis(failure);
}
// Allow default reporting to show analysis in console
return false;
}
private FailureAnalysis analyzeFailure(Throwable failure) {
// FailureAnalyzers have already run; check if analysis exists
// This is typically done internally by Spring Boot
return null; // Simplified for example
}
private void reportWithAnalysis(Throwable failure, FailureAnalysis analysis) {
System.err.println("Description: " + analysis.getDescription());
System.err.println("Action: " + analysis.getAction());
}
private void reportWithoutAnalysis(Throwable failure) {
System.err.println("Failure: " + failure.getMessage());
}
}BeanFactoryPostProcessor that applies lazy initialization to bean definitions that are not explicitly excluded and have not already had a value set.
package org.springframework.boot;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.core.Ordered;
/**
* BeanFactoryPostProcessor to set lazy-init on bean definitions that are not
* excluded and have not already had a value explicitly set.
*
* Note that SmartInitializingSingletons are automatically excluded from lazy
* initialization to ensure that their callback method is invoked.
*
* Beans that are in the infrastructure role are automatically excluded from
* lazy initialization, too.
*
* @since 2.2.0
* @see LazyInitializationExcludeFilter
*/
public final class LazyInitializationBeanFactoryPostProcessor
implements BeanFactoryPostProcessor, Ordered {
// Inherited from BeanFactoryPostProcessor
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException;
// Inherited from Ordered - returns Ordered.HIGHEST_PRECEDENCE
@Override
public int getOrder();
}This processor is automatically registered when SpringApplication.setLazyInitialization(true) is called. It processes all bean definitions and sets lazy-init=true on beans that:
lazy-init setting are not modifiedLazyInitializationExcludeFilter beans remain eagerROLE_INFRASTRUCTURE are automatically excludedThe processor automatically excludes:
afterSingletonsInstantiated() callbacks executeBeanDefinition.ROLE_INFRASTRUCTURELazyInitializationExcludeFilter beansThis processor is automatically registered when lazy initialization is enabled:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MyApplication.class);
// Automatically registers LazyInitializationBeanFactoryPostProcessor
app.setLazyInitialization(true);
app.run(args);
}
}In rare cases, you may need to register this processor manually:
import org.springframework.boot.LazyInitializationBeanFactoryPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class LazyConfig {
@Bean
public static LazyInitializationBeanFactoryPostProcessor lazyInitProcessor() {
return new LazyInitializationBeanFactoryPostProcessor();
}
}Note: Manual registration is rarely needed. Use SpringApplication.setLazyInitialization(true) instead.
This processor consults all registered LazyInitializationExcludeFilter beans to determine which beans should remain eagerly initialized:
import org.springframework.boot.LazyInitializationExcludeFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ExclusionConfig {
// This filter is automatically consulted by LazyInitializationBeanFactoryPostProcessor
@Bean
public static LazyInitializationExcludeFilter excludeHealthChecks() {
return LazyInitializationExcludeFilter.forBeanTypes(HealthIndicator.class);
}
}The processor runs at Ordered.HIGHEST_PRECEDENCE to ensure it executes before other BeanFactoryPostProcessor implementations that might depend on the final lazy-init state of beans.
Filter interface for excluding specific beans from having lazy initialization applied by LazyInitializationBeanFactoryPostProcessor when SpringApplication.setLazyInitialization(true) is enabled.
package org.springframework.boot;
import org.springframework.beans.factory.config.BeanDefinition;
/**
* Filter that can be used to exclude bean definitions from having their lazy-init set
* by the LazyInitializationBeanFactoryPostProcessor.
*
* Primarily intended to allow downstream projects to deal with edge-cases where lazy
* loading is not appropriate (such as beans that must be initialized at startup).
*
* NOTE: Beans of this type are instantiated very early in the application lifecycle,
* so they should generally be declared static and not have any dependencies.
*
* @since 2.2.0
*/
@FunctionalInterface
public interface LazyInitializationExcludeFilter {
/**
* Returns true if the specified bean definition should be excluded from
* having lazy-init automatically set.
*
* @param beanName the bean name
* @param beanDefinition the bean definition
* @param beanType the bean type
* @return true if lazy-init should not be automatically set
*/
boolean isExcluded(String beanName, BeanDefinition beanDefinition, Class<?> beanType);
/**
* Factory method that creates a filter for the given bean types.
* Beans matching any of the specified types will be excluded from lazy initialization.
*
* @param types the filtered types (beans assignable to these types will be excluded)
* @return a new filter instance
*/
static LazyInitializationExcludeFilter forBeanTypes(Class<?>... types);
}import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.LazyInitializationExcludeFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.scheduling.annotation.Scheduled;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MyApplication.class);
// Enable lazy initialization for faster startup
app.setLazyInitialization(true);
app.run(args);
}
// Example 1: Exclude specific framework types that don't work well with lazy init
@Bean
public static LazyInitializationExcludeFilter integrationLazyExcludeFilter() {
// Spring Integration flows must be eagerly initialized
return LazyInitializationExcludeFilter.forBeanTypes(IntegrationFlow.class);
}
// Example 2: Exclude beans with specific annotation
@Bean
public static LazyInitializationExcludeFilter scheduledTaskFilter() {
return (beanName, beanDefinition, beanType) -> {
// Eagerly initialize beans with @Scheduled methods
return beanType.getDeclaredMethods().length > 0 &&
Arrays.stream(beanType.getDeclaredMethods())
.anyMatch(m -> m.isAnnotationPresent(Scheduled.class));
};
}
// Example 3: Exclude critical beans by name pattern
@Bean
public static LazyInitializationExcludeFilter criticalBeansFilter() {
return (beanName, beanDefinition, beanType) -> {
// Eagerly initialize health indicators, data sources, and security beans
return beanName.endsWith("HealthIndicator") ||
beanName.endsWith("DataSource") ||
beanName.startsWith("security") ||
beanType.getSimpleName().contains("Security");
};
}
// Example 4: Exclude beans implementing specific interface
@Bean
public static LazyInitializationExcludeFilter startupRequiredFilter() {
return (beanName, beanDefinition, beanType) -> {
// Eagerly initialize beans that implement StartupRequired interface
return StartupRequired.class.isAssignableFrom(beanType);
};
}
}
// Marker interface for beans that must be initialized at startup
interface StartupRequired {
// Marker interface
}import org.springframework.boot.LazyInitializationExcludeFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.actuate.health.HealthIndicator;
import javax.sql.DataSource;
@Configuration
public class LazyInitializationConfig {
@Bean
public static LazyInitializationExcludeFilter productionLazyExcludeFilter() {
return (String beanName, BeanDefinition beanDef, Class<?> beanType) -> {
// 1. Exclude all health indicators for immediate health check availability
if (HealthIndicator.class.isAssignableFrom(beanType)) {
return true;
}
// 2. Exclude data sources and connection pools
if (DataSource.class.isAssignableFrom(beanType)) {
return true;
}
// 3. Exclude metrics and monitoring beans
if (beanName.contains("Metric") || beanName.contains("Monitoring")) {
return true;
}
// 4. Exclude security configuration beans
if (beanType.getPackage() != null &&
beanType.getPackage().getName().contains(".security")) {
return true;
}
// 5. Exclude beans with custom marker annotation
if (beanType.isAnnotationPresent(EagerInit.class)) {
return true;
}
return false;
};
}
}
// Custom annotation for marking beans that must be eagerly initialized
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface EagerInit {
}Static Bean Declaration: LazyInitializationExcludeFilter beans should be declared as static to ensure they're instantiated early in the Spring lifecycle before lazy initialization is applied.
No Dependencies: Filter beans should not have dependencies on other beans, as they're instantiated very early in the application startup process.
Multiple Filters: You can define multiple LazyInitializationExcludeFilter beans - all registered filters are consulted, and if any filter returns true for a bean, that bean will be eagerly initialized.
Performance Impact: While lazy initialization improves startup time, excluding too many beans defeats the purpose. Be selective about which beans must be eagerly initialized.
Edge Cases: Certain Spring features don't work well with lazy initialization:
@Scheduled)// Pattern 1: Exclude by type hierarchy
LazyInitializationExcludeFilter.forBeanTypes(
DataSource.class,
HealthIndicator.class,
MetricReader.class
)
// Pattern 2: Exclude by name pattern
(name, def, type) -> name.matches(".*DataSource|.*HealthIndicator|.*Metrics.*")
// Pattern 3: Exclude by package
(name, def, type) -> type.getPackage() != null &&
type.getPackage().getName().startsWith("com.myapp.critical")
// Pattern 4: Exclude by annotation presence
(name, def, type) -> type.isAnnotationPresent(EagerInit.class) ||
Arrays.stream(type.getMethods())
.anyMatch(m -> m.isAnnotationPresent(Scheduled.class))
// Pattern 5: Combine multiple conditions
(name, def, type) ->
HealthIndicator.class.isAssignableFrom(type) ||
DataSource.class.isAssignableFrom(type) ||
name.endsWith("Security") ||
type.isAnnotationPresent(EagerInit.class)Early-stage registry for sharing expensive objects before ApplicationContext is available.
package org.springframework.boot.bootstrap;
import java.util.function.Supplier;
import org.springframework.context.ApplicationListener;
/**
* Simple object registry available during startup and Environment post-processing.
* Registry uses Class as key, so only one instance per type can be stored.
*/
public interface BootstrapRegistry {
/**
* Register a specific type. Replaces existing registration if present.
*
* @param type the instance type
* @param instanceSupplier the supplier for the instance
*/
<T> void register(Class<T> type, InstanceSupplier<T> instanceSupplier);
/**
* Register a specific type only if not already registered.
*
* @param type the instance type
* @param instanceSupplier the supplier for the instance
*/
<T> void registerIfAbsent(Class<T> type, InstanceSupplier<T> instanceSupplier);
/**
* Check if a registration exists for the given type.
*
* @param type the instance type
* @return true if already registered
*/
<T> boolean isRegistered(Class<T> type);
/**
* Get the registered instance supplier for the given type.
*
* @param type the instance type
* @return the registered supplier or null
*/
<T> InstanceSupplier<T> getRegisteredInstanceSupplier(Class<T> type);
/**
* Add a listener called when BootstrapContext is closed.
*
* @param listener the listener to add
*/
void addCloseListener(ApplicationListener<BootstrapContextClosedEvent> listener);
/**
* Supplier for instance creation.
*/
@FunctionalInterface
interface InstanceSupplier<T> {
/**
* Get or create the instance.
*
* @param context the bootstrap context for accessing other instances
* @return the instance (may be null)
*/
T get(BootstrapContext context);
/**
* Get the scope (SINGLETON or PROTOTYPE).
* Default is SINGLETON.
*/
default Scope getScope() {
return Scope.SINGLETON;
}
/**
* Return a new supplier with different scope.
*/
default InstanceSupplier<T> withScope(Scope scope) {
InstanceSupplier<T> parent = this;
return new InstanceSupplier<T>() {
@Override
public T get(BootstrapContext context) {
return parent.get(context);
}
@Override
public Scope getScope() {
return scope;
}
};
}
/**
* Create supplier for an existing instance.
*/
static <T> InstanceSupplier<T> of(T instance) {
return (context) -> instance;
}
/**
* Create supplier from a Supplier.
*/
static <T> InstanceSupplier<T> from(Supplier<T> supplier) {
return (context) -> supplier.get();
}
}
/**
* Scope for instances.
*/
enum Scope {
/** Instance created once and reused */
SINGLETON,
/** New instance created on each access */
PROTOTYPE
}
}package org.springframework.boot.bootstrap;
/**
* Callback interface for initializing a BootstrapRegistry before it is used.
*/
@FunctionalInterface
public interface BootstrapRegistryInitializer {
/**
* Initialize the given BootstrapRegistry with any required registrations.
* Called very early in application lifecycle, before Environment is prepared.
*
* @param registry the registry to initialize
*/
void initialize(BootstrapRegistry registry);
}import org.springframework.boot.SpringApplication;
import org.springframework.boot.bootstrap.BootstrapRegistryInitializer;
import org.springframework.boot.bootstrap.BootstrapRegistry;
import org.springframework.boot.bootstrap.BootstrapContext;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.support.GenericApplicationContext;
// 1. Define an expensive service
class ExpensiveService {
public ExpensiveService() {
System.out.println("Creating expensive service (simulating delay)...");
try { Thread.sleep(2000); } catch (InterruptedException e) {}
}
public String getData() {
return "Important data";
}
}
// 2. Define a logger service that depends on ExpensiveService
class LoggerService {
private final ExpensiveService expensiveService;
public LoggerService(ExpensiveService expensiveService) {
this.expensiveService = expensiveService;
System.out.println("Created logger with data: " + expensiveService.getData());
}
}
// 3. Bootstrap registry initializer
class MyBootstrapInitializer implements BootstrapRegistryInitializer {
@Override
public void initialize(BootstrapRegistry registry) {
// Register expensive service (singleton scope by default)
registry.register(ExpensiveService.class,
context -> new ExpensiveService());
// Register logger service that depends on expensive service
registry.register(LoggerService.class,
context -> {
ExpensiveService expensiveService = context.get(ExpensiveService.class);
return new LoggerService(expensiveService);
});
// Register a prototype-scoped service (new instance each time)
registry.register(RequestCounter.class,
BootstrapRegistry.InstanceSupplier
.from(() -> new RequestCounter())
.withScope(BootstrapRegistry.Scope.PROTOTYPE));
// Add close listener to migrate instances to ApplicationContext
registry.addCloseListener(event -> {
System.out.println("Bootstrap context closing, migrating to ApplicationContext");
// Get instance from bootstrap context
ExpensiveService service = event.getBootstrapContext()
.get(ExpensiveService.class);
// Register as Spring bean
GenericApplicationContext appContext =
(GenericApplicationContext) event.getApplicationContext();
appContext.registerBean("expensiveService",
ExpensiveService.class,
() -> service);
});
}
}
// 4. Listener that uses bootstrap context
class EnvironmentInitializer
implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
BootstrapContext bootstrapContext = event.getBootstrapContext();
// Access registered instances
ExpensiveService service = bootstrapContext.get(ExpensiveService.class);
System.out.println("Using bootstrap service: " + service.getData());
// Access prototype-scoped instance (new instance each time)
RequestCounter counter1 = bootstrapContext.get(RequestCounter.class);
RequestCounter counter2 = bootstrapContext.get(RequestCounter.class);
System.out.println("Counters are different: " + (counter1 != counter2));
// Check if type is registered
boolean hasLogger = bootstrapContext.isRegistered(LoggerService.class);
System.out.println("Logger registered: " + hasLogger);
}
}
// 5. Main application
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MyApplication.class);
// Add bootstrap registry initializer
app.addBootstrapRegistryInitializer(new MyBootstrapInitializer());
// Add early listener
app.addListeners(new EnvironmentInitializer());
ConfigurableApplicationContext context = app.run(args);
// After migration, can access as regular bean
ExpensiveService service = context.getBean(ExpensiveService.class);
System.out.println("Using bean: " + service.getData());
}
}
class RequestCounter {
private static int instanceCount = 0;
private final int id;
public RequestCounter() {
this.id = ++instanceCount;
}
public int getId() {
return id;
}
}Indicates that a class provides Spring Boot application configuration. Alternative to Spring's @Configuration that enables automatic discovery.
package org.springframework.boot;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AliasFor;
import java.lang.annotation.*;
/**
* Indicates that a class provides Spring Boot application configuration.
* Alternative to @Configuration for enabling automatic discovery (e.g., in tests).
*
* Application should only include ONE @SpringBootConfiguration.
* Most applications inherit this from @SpringBootApplication.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
/**
* Specify whether @Bean methods should be proxied for inter-bean references.
* When true: Creates CGLIB subclass for enforcing bean lifecycle
* When false: "Lite mode" - no proxying, better startup performance
*
* Default: true
*/
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}// Example 1: Standard usage (inherited from @SpringBootApplication)
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
// Example 2: Explicit @SpringBootConfiguration (rare)
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
// Example 3: Lite mode for improved startup performance
@SpringBootConfiguration(proxyBeanMethods = false)
@EnableAutoConfiguration
@ComponentScan
public class MyApplication {
@Bean
public MyService myService() {
return new MyService();
}
@Bean
public MyController myController() {
// Direct instantiation - no proxy interception
// Cannot call myService() here to get singleton
return new MyController(new MyService()); // Creates new instance
}
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
// Example 4: Full mode (default) with inter-bean calls
@SpringBootConfiguration(proxyBeanMethods = true) // Default behavior
public class MyApplication {
@Bean
public MyService myService() {
return new MyService();
}
@Bean
public MyController myController() {
// Calls myService() method, which is proxied
// Returns the singleton bean, not new instance
return new MyController(myService()); // Gets singleton
}
}
// Example 5: Test configuration discovery
@SpringBootConfiguration
class TestConfiguration {
@Bean
public MockBean mockBean() {
return new MockBean();
}
}
// Test will automatically find TestConfiguration due to @SpringBootConfiguration
@SpringBootTest
class MyTest {
@Autowired
private MockBean mockBean;
@Test
void testSomething() {
// mockBean is automatically available
}
}@SpringBootConfiguration@SpringBootApplication@SpringBootTestpackage com.example.simple;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SimpleApplication {
public static void main(String[] args) {
// Most basic startup
SpringApplication.run(SimpleApplication.class, args);
}
}package com.example.custom;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.Banner;
import org.springframework.boot.WebApplicationType;
import java.util.HashMap;
import java.util.Map;
@SpringBootApplication
public class CustomApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(CustomApplication.class);
// Web application type
app.setWebApplicationType(WebApplicationType.SERVLET);
// Banner configuration
app.setBannerMode(Banner.Mode.CONSOLE);
// Default properties
Map<String, Object> defaults = new HashMap<>();
defaults.put("server.port", "8080");
defaults.put("spring.application.name", "custom-app");
defaults.put("logging.level.root", "INFO");
defaults.put("logging.level.com.example", "DEBUG");
app.setDefaultProperties(defaults);
// Additional profiles
app.setAdditionalProfiles("metrics", "health");
// Startup configuration
app.setLogStartupInfo(true);
app.setRegisterShutdownHook(true);
// Run
app.run(args);
}
}package com.example.runners;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.ApplicationArguments;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;
@SpringBootApplication
public class RunnersApplication {
public static void main(String[] args) {
SpringApplication.run(RunnersApplication.class, args);
}
@Bean
@Order(1)
public CommandLineRunner initDatabase() {
return args -> {
System.out.println("Initializing database...");
// Database initialization
};
}
@Bean
@Order(2)
public ApplicationRunner loadData() {
return args -> {
System.out.println("Loading initial data...");
if (args.containsOption("seed")) {
System.out.println("Seeding database with test data");
// Seed data
}
};
}
@Bean
@Order(3)
public ApplicationRunner performHealthCheck() {
return args -> {
System.out.println("Performing health check...");
// Health check logic
};
}
}package com.example.hierarchy;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@SpringBootApplication
public class HierarchyApplication {
public static void main(String[] args) {
new SpringApplicationBuilder()
.sources(ParentConfiguration.class)
.web(WebApplicationType.NONE)
.properties("spring.application.name=parent-context")
.child(ChildConfiguration.class)
.web(WebApplicationType.SERVLET)
.properties(
"spring.application.name=child-context",
"server.port=8080"
)
.run(args);
}
}
@Configuration
class ParentConfiguration {
@Bean
public DataSource dataSource() {
// Create and configure DataSource
return new HikariDataSource();
}
@Bean
public SharedService sharedService() {
return new SharedService();
}
}
@Configuration
class ChildConfiguration {
@Bean
public WebController webController(DataSource dataSource, SharedService sharedService) {
// Can inject beans from parent context
return new WebController(dataSource, sharedService);
}
@Bean
public RestController restController(DataSource dataSource) {
return new RestController(dataSource);
}
}package com.example.bootstrap;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.BootstrapRegistryInitializer;
import org.springframework.boot.BootstrapRegistry;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@SpringBootApplication
public class BootstrapApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(BootstrapApplication.class);
// Register bootstrap initializer
app.addBootstrapRegistryInitializer(registry -> {
registry.register(ConfigurationService.class,
context -> new ConfigurationService());
registry.addCloseListener(event -> {
ConfigurationService service =
event.getBootstrapContext().get(ConfigurationService.class);
// Migrate to application context
GenericApplicationContext appContext =
(GenericApplicationContext) event.getApplicationContext();
appContext.registerBean("configurationService",
ConfigurationService.class,
() -> service);
});
});
// Add early listener
app.addListeners((ApplicationListener<ApplicationEnvironmentPreparedEvent>) event -> {
ConfigurationService service =
event.getBootstrapContext().get(ConfigurationService.class);
// Use service during environment preparation
service.loadConfiguration(event.getEnvironment());
});
app.run(args);
}
}
class ConfigurationService {
public void loadConfiguration(ConfigurableEnvironment environment) {
System.out.println("Loading configuration during bootstrap");
// Configuration loading logic
}
}SpringApplication - Not thread-safe, create separate instance per threadSpringApplicationBuilder - Not thread-safe, create separate instance per threadBootstrapRegistry - Thread-safe, can be accessed from multiple threadsApplicationArguments - Thread-safe, immutable after creationIf any exception occurs during startup:
@SpringBootApplication
public class CliApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(CliApplication.class);
app.setWebApplicationType(WebApplicationType.NONE);
System.exit(SpringApplication.exit(app.run(args)));
}
@Override
public void run(String... args) throws Exception {
// CLI logic
if (args.length == 0) {
System.err.println("Usage: app <command>");
throw new IllegalArgumentException("No command provided");
}
String command = args[0];
switch (command) {
case "import" -> importData();
case "export" -> exportData();
default -> throw new IllegalArgumentException("Unknown command: " + command);
}
}
private void importData() { /* implementation */ }
private void exportData() { /* implementation */ }
}@SpringBootApplication
public class ConditionalApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(ConditionalApplication.class);
// Conditionally enable features based on arguments
if (Arrays.asList(args).contains("--enable-caching")) {
app.setAdditionalProfiles("caching");
}
if (Arrays.asList(args).contains("--enable-security")) {
app.setAdditionalProfiles("security");
}
app.run(args);
}
}@SpringBootApplication
public class DynamicApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(DynamicApplication.class);
// Dynamic configuration based on environment
String env = System.getProperty("APP_ENV", "dev");
Map<String, Object> props = new HashMap<>();
if ("production".equals(env)) {
props.put("logging.level.root", "WARN");
props.put("server.port", "80");
} else {
props.put("logging.level.root", "DEBUG");
props.put("server.port", "8080");
}
app.setDefaultProperties(props);
app.run(args);
}
}Problem: Attempting to configure SpringApplication after calling run().
// WRONG - configuration after run() has no effect
SpringApplication app = new SpringApplication(MyApp.class);
ConfigurableApplicationContext context = app.run(args);
app.setBannerMode(Banner.Mode.OFF); // TOO LATE - context already createdSolution: Configure before calling run().
// CORRECT
SpringApplication app = new SpringApplication(MyApp.class);
app.setBannerMode(Banner.Mode.OFF);
app.setWebApplicationType(WebApplicationType.SERVLET);
ConfigurableApplicationContext context = app.run(args);Why It Fails: The run() method creates and initializes the application context. Any configuration changes after this point are ignored because the context is already built.
Problem: Sharing a single SpringApplication instance across multiple threads.
// WRONG - SpringApplication is not thread-safe
private static final SpringApplication app = new SpringApplication(MyApp.class);
public void startInThread() {
new Thread(() -> app.run(args)).start(); // UNSAFE
new Thread(() -> app.run(args)).start(); // UNSAFE
}Solution: Create a new instance per thread or use proper synchronization.
// CORRECT - new instance per thread
public void startInThread() {
new Thread(() -> {
SpringApplication app = new SpringApplication(MyApp.class);
app.run(args);
}).start();
}Why It Fails: SpringApplication maintains mutable state during startup. Concurrent access causes race conditions and unpredictable behavior.
Problem: Enabling strict circular reference checking breaks existing code.
// Configuration
SpringApplication app = new SpringApplication(MyApp.class);
app.setAllowCircularReferences(false); // Strict mode
app.run(args);
// Beans with circular dependency
@Service
class ServiceA {
@Autowired private ServiceB serviceB; // Circular!
}
@Service
class ServiceB {
@Autowired private ServiceA serviceA; // Circular!
}Error:
BeanCurrentlyInCreationException: Circular dependency between 'serviceA' and 'serviceB'Solution: Refactor to eliminate circular dependencies using one of these patterns:
// Pattern 1: Constructor injection + @Lazy
@Service
class ServiceA {
private final ServiceB serviceB;
public ServiceA(@Lazy ServiceB serviceB) {
this.serviceB = serviceB;
}
}
// Pattern 2: Setter injection
@Service
class ServiceA {
private ServiceB serviceB;
@Autowired
public void setServiceB(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
// Pattern 3: Refactor to eliminate dependency
@Service
class ServiceA {
private final EventPublisher eventPublisher;
public ServiceA(EventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
}Best Practice: Design beans with clear dependency hierarchies. Circular dependencies indicate poor design.
Problem: Creating SpringApplication but forgetting to call run().
// WRONG - application never starts
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MyApp.class);
app.setBannerMode(Banner.Mode.OFF);
// Missing: app.run(args);
System.out.println("Application configured"); // Prints, but app doesn't start
}Solution: Always call run() to start the application.
// CORRECT
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MyApp.class);
app.setBannerMode(Banner.Mode.OFF);
ConfigurableApplicationContext context = app.run(args); // MUST call run()
}Why It Fails: Without calling run(), the Spring container is never created, no beans are initialized, and the application exits immediately.
Problem: Wrong WebApplicationType for the available dependencies.
// WRONG - forcing REACTIVE when only servlet dependencies exist
SpringApplication app = new SpringApplication(MyApp.class);
app.setWebApplicationType(WebApplicationType.REACTIVE);
app.run(args);Error:
ApplicationContextException: Unable to start ReactiveWebApplicationContext due to missing reactive dependenciesSolution: Let Spring Boot auto-detect or ensure dependencies match:
// Option 1: Auto-detect (recommended)
SpringApplication app = new SpringApplication(MyApp.class);
// Don't set web type - let Boot detect from classpath
app.run(args);
// Option 2: Explicit type matching dependencies
// For servlet apps (spring-boot-starter-web)
app.setWebApplicationType(WebApplicationType.SERVLET);
// For reactive apps (spring-boot-starter-webflux)
app.setWebApplicationType(WebApplicationType.REACTIVE);
// For non-web apps
app.setWebApplicationType(WebApplicationType.NONE);Dependencies:
WebApplicationType.SERVLET requires spring-boot-starter-webWebApplicationType.REACTIVE requires spring-boot-starter-webfluxWebApplicationType.NONE requires neitherProblem: Enabling lazy initialization hides startup errors.
// Lazy initialization enabled
SpringApplication app = new SpringApplication(MyApp.class);
app.setLazyInitialization(true);
app.run(args); // Starts successfully...
// But failures occur later at runtime
@Service
class BrokenService {
@Autowired
private NonExistentBean bean; // Error only when first accessed!
}Solution: Use lazy initialization carefully and add health checks:
// Option 1: Disable for critical beans
@Configuration
public class Config {
@Bean
public LazyInitializationExcludeFilter lazyExcludeFilter() {
return (name, definition, type) -> {
// Always eagerly initialize critical beans
return name.endsWith("HealthIndicator") ||
name.endsWith("DataSource") ||
Critical.class.isAssignableFrom(type);
};
}
}
// Option 2: Validate on startup with health check
@Component
class StartupValidator implements ApplicationRunner {
@Autowired
private BrokenService service;
@Override
public void run(ApplicationArguments args) {
service.validateConfiguration(); // Force initialization
}
}Best Practice: Only use lazy initialization in development or when you have comprehensive health checks.
Problem: Multiple classes annotated with @SpringBootApplication in the classpath.
// Class 1
@SpringBootApplication
public class MainApp {
public static void main(String[] args) {
SpringApplication.run(MainApp.class, args);
}
}
// Class 2 (e.g., in test package)
@SpringBootApplication
public class TestApp {
// Another main class
}Error (in tests):
IllegalStateException: Found multiple @SpringBootConfiguration annotated classesSolution: Use @SpringBootConfiguration only once per application:
// Main application
@SpringBootApplication
public class MainApp { }
// Test configuration - use @TestConfiguration instead
@TestConfiguration
@EnableAutoConfiguration
@ComponentScan
public class TestConfig {
// Test-specific beans
}
// Or use @SpringBootTest with specific classes
@SpringBootTest(classes = MainApp.class)
class MyTest { }Rule: Only one @SpringBootApplication or @SpringBootConfiguration per application context.
Problem: Runners execute in wrong order, causing dependencies to fail.
@Component
class DataLoader implements CommandLineRunner {
@Override
public void run(String... args) {
loadData(); // Needs database to be ready
}
}
@Component
class DatabaseInitializer implements CommandLineRunner {
@Override
public void run(String... args) {
initializeDatabase(); // Must run first
}
}Solution: Use @Order or implement Ordered:
@Component
@Order(1) // Runs first
class DatabaseInitializer implements CommandLineRunner {
@Override
public void run(String... args) {
initializeDatabase();
}
}
@Component
@Order(2) // Runs second
class DataLoader implements CommandLineRunner {
@Override
public void run(String... args) {
loadData();
}
}
// Or implement Ordered
@Component
class DatabaseInitializer implements CommandLineRunner, Ordered {
@Override
public void run(String... args) {
initializeDatabase();
}
@Override
public int getOrder() {
return 1;
}
}Best Practice: Always specify order for interdependent runners. Lower numbers execute first.
Problem: Generating exit code but not propagating it to JVM.
// WRONG - exit code generated but JVM exits with 0
@SpringBootApplication
public class MyApp {
public static void main(String[] args) {
ConfigurableApplicationContext context =
SpringApplication.run(MyApp.class, args);
int exitCode = SpringApplication.exit(context, () -> 1);
// Missing: System.exit(exitCode);
}
}
@Component
class FailingService implements ExitCodeGenerator {
@Override
public int getExitCode() {
return 1; // Indicates failure
}
}Solution: Always call System.exit() with the generated exit code:
// CORRECT
@SpringBootApplication
public class MyApp {
public static void main(String[] args) {
ConfigurableApplicationContext context =
SpringApplication.run(MyApp.class, args);
int exitCode = SpringApplication.exit(context);
System.exit(exitCode); // Propagate to JVM
}
}
// Better: Handle in one line
public static void main(String[] args) {
System.exit(SpringApplication.exit(
SpringApplication.run(MyApp.class, args)
));
}Why It Matters: Without System.exit(), the JVM always exits with code 0, making failure detection impossible in scripts and CI/CD.
Problem: Changing environment properties after context has been refreshed.
@SpringBootApplication
public class MyApp implements ApplicationRunner {
@Autowired
private ConfigurableEnvironment environment;
@Override
public void run(ApplicationArguments args) {
// TOO LATE - beans already created with old property values
environment.getSystemProperties().put("app.name", "NewName");
}
}Solution: Modify environment before refresh using EnvironmentPostProcessor or ApplicationContextInitializer:
// CORRECT - using EnvironmentPostProcessor
public class PropertyModifier implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(
ConfigurableEnvironment environment,
SpringApplication application) {
// Runs BEFORE beans are created
Map<String, Object> props = new HashMap<>();
props.put("app.name", "NewName");
environment.getPropertySources().addFirst(
new MapPropertySource("custom", props)
);
}
}
// Register in META-INF/spring.factories
// org.springframework.boot.env.EnvironmentPostProcessor=\
// com.example.PropertyModifier
// Or use ApplicationContextInitializer
public class EnvInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext context) {
ConfigurableEnvironment env = context.getEnvironment();
// Modify environment before refresh
env.getSystemProperties().put("app.name", "NewName");
}
}
// Add to application
SpringApplication app = new SpringApplication(MyApp.class);
app.addInitializers(new EnvInitializer());
app.run(args);Key Insight: Property changes only affect beans created after the change. Most beans are created during context refresh, so changes must happen before that.
Exception thrown when the AOT initializer class cannot be found during AOT-enabled startup.
package org.springframework.boot;
/**
* Exception thrown when the AOT initializer couldn't be found.
* Occurs during startup with AOT mode enabled when the expected AOT-generated
* initializer class is not present in the classpath.
*
* Thread Safety: Immutable exception, safe to throw from any thread.
*
* @since 3.2.6
*/
public class AotInitializerNotFoundException extends RuntimeException {
/**
* Create a new exception for a missing AOT initializer.
*
* @param mainClass the main application class
* @param initializerClassName the name of the expected initializer class that was not found
*/
public AotInitializerNotFoundException(Class<?> mainClass, String initializerClassName);
/**
* Return the main application class.
*
* @return the main class
*/
public Class<?> getMainClass();
}When This Occurs:
Example:
// This exception is thrown internally by Spring Boot when:
// 1. Application starts with AOT mode enabled
// 2. Expected initializer class like "com.example.MyApplication__ApplicationContextInitializer" is not found
// Typically indicates build configuration issue - AOT plugin didn't run or output wasn't includedEntry point for AOT (Ahead-of-Time) processing of a Spring Boot application. Generates optimized startup code and configuration during build time.
package org.springframework.boot;
import org.springframework.context.aot.ContextAotProcessor;
/**
* Entry point for AOT processing of a SpringApplication.
* Extends Spring Framework's ContextAotProcessor to provide Spring Boot-specific
* AOT processing capabilities.
*
* This class is for internal use only and is typically invoked by build tools
* during the AOT compilation phase.
*
* Thread Safety: Not thread-safe. Should only be used from build tool context.
*
* @since 3.0.0
*/
public class SpringApplicationAotProcessor extends ContextAotProcessor {
/**
* Create a new processor for the specified application and settings.
*
* @param application the application main class to process
* @param settings the general AOT processor settings (output paths, artifact IDs)
* @param applicationArgs the arguments to provide to the main method during processing
*/
public SpringApplicationAotProcessor(Class<?> application, Settings settings, String[] applicationArgs);
/**
* Prepare the application context for AOT processing.
* Invokes the application's main method to bootstrap the context, then
* captures it before full startup completes.
*
* @param application the application main class
* @return the prepared GenericApplicationContext ready for AOT processing
*/
@Override
protected GenericApplicationContext prepareApplicationContext(Class<?> application);
/**
* Main entry point for command-line invocation during build.
* Expected arguments:
* 1. applicationMainClass - fully qualified main class name
* 2. sourceOutput - path for generated source files
* 3. resourceOutput - path for generated resource files
* 4. classOutput - path for generated class files
* 5. groupId - Maven/Gradle group ID
* 6. artifactId - Maven/Gradle artifact ID
* 7+ originalArgs - optional application arguments
*
* @param args command line arguments
* @throws Exception if AOT processing fails
*/
public static void main(String[] args) throws Exception;
}Usage Context: This processor is invoked automatically by Spring Boot build plugins:
spring-boot-maven-plugin process-aot goalorg.springframework.boot plugin's processAot taskAOT Processing Flow:
SpringApplicationAotProcessor.main()sourceOutputresourceOutputclassOutputGenerated Artifacts:
__ApplicationContextInitializer.java - Context initialization code__BeanDefinitions.java - Bean definition registrations__BeanFactoryRegistrations.java - Bean factory configurationsMETA-INF/native-image/ - GraalVM native image configurationExample Maven Configuration:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>process-aot</goal>
</goals>
</execution>
</executions>
</plugin>