docs
Package: org.springframework.boot.bootstrap
Since: 4.0.0
Simple object registry available during startup and environment post-processing, before the ApplicationContext is prepared. Used for expensive-to-create objects or objects that need to be shared early in the application lifecycle.
The bootstrap package provides a lightweight registry and context system available during the earliest stages of Spring Boot application startup. This allows EnvironmentPostProcessors and other early initialization code to share objects before the main ApplicationContext is created.
Key Use Cases:
Register instances that will be available during early startup phases.
package org.springframework.boot.bootstrap;
import org.springframework.context.ApplicationListener;
/**
* A simple object registry available during startup and Environment post-processing.
* @since 4.0.0
*/
public interface BootstrapRegistry {
/**
* Register a specific type. Replaces existing registration if not yet obtained as singleton.
* @param type the instance type
* @param instanceSupplier the instance supplier
*/
<T> void register(Class<T> type, InstanceSupplier<T> instanceSupplier);
/**
* Register a type only if not already present.
* @param type the instance type
* @param instanceSupplier the instance supplier
*/
<T> void registerIfAbsent(Class<T> type, InstanceSupplier<T> instanceSupplier);
/**
* Check if a type is registered.
* @param type the instance type
* @return true if registered
*/
<T> boolean isRegistered(Class<T> type);
/**
* Get the registered InstanceSupplier for a type.
* @param type the instance type
* @return the InstanceSupplier or null
*/
<T> InstanceSupplier<T> getRegisteredInstanceSupplier(Class<T> type);
/**
* Add listener called when BootstrapContext closes and ApplicationContext is prepared.
* @param listener the listener to add
*/
void addCloseListener(ApplicationListener<BootstrapContextClosedEvent> listener);
/**
* Supplier for instance creation.
* @param <T> the instance type
*/
@FunctionalInterface
interface InstanceSupplier<T> {
/**
* Create the instance when needed.
* @param context the BootstrapContext for accessing other instances
* @return the instance or null
*/
T get(BootstrapContext context);
/**
* Get the scope of the instance.
* @return the scope (default: SINGLETON)
*/
default Scope getScope() {
return Scope.SINGLETON;
}
/**
* Return new InstanceSupplier with updated scope.
* @param scope the new scope
* @return new InstanceSupplier with specified scope
*/
default InstanceSupplier<T> withScope(Scope scope);
/**
* Create InstanceSupplier from an instance.
* @param instance the instance
* @return new InstanceSupplier
*/
static <T> InstanceSupplier<T> of(T instance);
/**
* Create InstanceSupplier from a Supplier.
* @param supplier the supplier
* @return new InstanceSupplier
*/
static <T> InstanceSupplier<T> from(Supplier<T> supplier);
}
/**
* Instance scope.
*/
enum Scope {
/** Singleton - created once and cached */
SINGLETON,
/** Prototype - created every time it's requested */
PROTOTYPE
}
}Usage Example:
import org.springframework.boot.bootstrap.BootstrapRegistry;
import org.springframework.boot.bootstrap.BootstrapRegistry.InstanceSupplier;
import org.springframework.boot.bootstrap.BootstrapRegistry.Scope;
public void registerBootstrapInstance(BootstrapRegistry registry) {
// Register singleton instance (created once, cached)
registry.register(MyExpensiveService.class,
InstanceSupplier.from(() -> new MyExpensiveService()));
// Register prototype instance (created each time)
registry.register(RequestContext.class,
InstanceSupplier.from(() -> new RequestContext())
.withScope(Scope.PROTOTYPE));
// Register with dependency on other bootstrap instance
registry.register(ConfigLoader.class, context -> {
MyExpensiveService service = context.get(MyExpensiveService.class);
return new ConfigLoader(service);
});
// Conditional registration
registry.registerIfAbsent(DefaultLogger.class,
InstanceSupplier.of(new DefaultLogger()));
}Read-only access to registered instances during environment post-processing.
package org.springframework.boot.bootstrap;
import java.util.function.Supplier;
/**
* Read-only access to bootstrap instances.
* @since 4.0.0
*/
public interface BootstrapContext {
/**
* Get a registered instance. Throws if not registered.
* @param type the instance type
* @return the instance
* @throws IllegalStateException if not registered
*/
<T> T get(Class<T> type) throws IllegalStateException;
/**
* Get instance or return default value.
* @param type the instance type
* @param other the default value
* @return the instance or default
*/
<T> T getOrElse(Class<T> type, T other);
/**
* Get instance or supply default value.
* @param type the instance type
* @param other supplier for default value
* @return the instance or supplied default
*/
<T> T getOrElseSupply(Class<T> type, Supplier<T> other);
/**
* Get instance or throw custom exception.
* @param type the instance type
* @param exceptionSupplier supplier for exception
* @return the instance
* @throws X if not registered
*/
<T, X extends Throwable> T getOrElseThrow(Class<T> type,
Supplier<? extends X> exceptionSupplier) throws X;
/**
* Check if type is registered.
* @param type the instance type
* @return true if registered
*/
<T> boolean isRegistered(Class<T> type);
}Usage Example:
import org.springframework.boot.EnvironmentPostProcessor;
import org.springframework.boot.bootstrap.BootstrapContext;
import org.springframework.core.env.ConfigurableEnvironment;
public class MyEnvironmentPostProcessor implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
BootstrapContext bootstrap = application.getBootstrapContext();
// Get required instance
MyExpensiveService service = bootstrap.get(MyExpensiveService.class);
// Get optional instance with default
Logger logger = bootstrap.getOrElse(Logger.class, new ConsoleLogger());
// Get with lazy default
ConfigLoader loader = bootstrap.getOrElseSupply(ConfigLoader.class,
() -> new ConfigLoader());
// Get or throw custom exception
DataSource dataSource = bootstrap.getOrElseThrow(DataSource.class,
() -> new IllegalStateException("DataSource not configured"));
// Conditional logic
if (bootstrap.isRegistered(CacheManager.class)) {
CacheManager cache = bootstrap.get(CacheManager.class);
// Use cache
}
}
}Combined registry and context interface for full control.
package org.springframework.boot.bootstrap;
/**
* Configurable BootstrapContext combining registry and context interfaces.
* @since 4.0.0
*/
public interface ConfigurableBootstrapContext extends BootstrapRegistry, BootstrapContext {
// Inherits all methods from both BootstrapRegistry and BootstrapContext
}Callback interface for initializing the BootstrapRegistry before use.
package org.springframework.boot.bootstrap;
/**
* Callback interface for initializing BootstrapRegistry.
* @since 4.0.0
*/
@FunctionalInterface
public interface BootstrapRegistryInitializer {
/**
* Initialize the BootstrapRegistry.
* @param registry the registry to initialize
*/
void initialize(BootstrapRegistry registry);
}Usage Example:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.bootstrap.BootstrapRegistryInitializer;
public class MyApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MyApplication.class);
// Add bootstrap initializer
app.addBootstrapRegistryInitializer(registry -> {
// Register instances needed during environment post-processing
registry.register(MyService.class,
context -> new MyService());
// Add close listener to migrate to ApplicationContext
registry.addCloseListener(event -> {
MyService service = event.getBootstrapContext()
.get(MyService.class);
// Register as Spring bean
event.getApplicationContext()
.getBeanFactory()
.registerSingleton("myService", service);
});
});
app.run(args);
}
}Event published when BootstrapContext closes and ApplicationContext is prepared.
package org.springframework.boot.bootstrap;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ConfigurableApplicationContext;
/**
* Event published when BootstrapContext is closed.
* @since 4.0.0
*/
public class BootstrapContextClosedEvent extends ApplicationEvent {
/**
* Create new event.
* @param applicationContext the application context
* @param bootstrapContext the bootstrap context
*/
public BootstrapContextClosedEvent(ConfigurableApplicationContext applicationContext,
ConfigurableBootstrapContext bootstrapContext);
/**
* Get the BootstrapContext being closed.
* @return the bootstrap context
*/
public ConfigurableBootstrapContext getBootstrapContext();
/**
* Get the prepared ApplicationContext.
* @return the application context
*/
public ConfigurableApplicationContext getApplicationContext();
}Usage Example:
import org.springframework.boot.bootstrap.BootstrapContextClosedEvent;
import org.springframework.boot.bootstrap.BootstrapRegistry;
import org.springframework.context.ApplicationListener;
// Register close listener during bootstrap initialization
registry.addCloseListener(new ApplicationListener<BootstrapContextClosedEvent>() {
@Override
public void onApplicationEvent(BootstrapContextClosedEvent event) {
// Access bootstrap instances
MyService service = event.getBootstrapContext()
.get(MyService.class);
// Migrate to ApplicationContext
event.getApplicationContext()
.getBeanFactory()
.registerSingleton("myService", service);
// Perform cleanup
service.initialize();
}
});Default implementation of ConfigurableBootstrapContext.
package org.springframework.boot.bootstrap;
/**
* Default implementation of ConfigurableBootstrapContext.
* @since 4.0.0
*/
public class DefaultBootstrapContext implements ConfigurableBootstrapContext {
/**
* Create new DefaultBootstrapContext.
*/
public DefaultBootstrapContext();
// Implements all methods from ConfigurableBootstrapContext
}Share expensive resources across multiple EnvironmentPostProcessors:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.EnvironmentPostProcessor;
import org.springframework.boot.bootstrap.BootstrapRegistry;
import org.springframework.core.env.ConfigurableEnvironment;
public class MyApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MyApplication.class);
// Register expensive resource once
app.addBootstrapRegistryInitializer(registry -> {
registry.register(DatabaseMetadataLoader.class, context -> {
System.out.println("Creating expensive DatabaseMetadataLoader...");
return new DatabaseMetadataLoader();
});
});
app.run(args);
}
}
// Multiple post-processors can share the same instance
class FirstPostProcessor implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
DatabaseMetadataLoader loader =
application.getBootstrapContext().get(DatabaseMetadataLoader.class);
// Use loader - created only once
}
}
class SecondPostProcessor implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
DatabaseMetadataLoader loader =
application.getBootstrapContext().get(DatabaseMetadataLoader.class);
// Uses same instance as FirstPostProcessor
}
}Migrate bootstrap instances into the ApplicationContext:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.bootstrap.BootstrapRegistry;
public class MyApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MyApplication.class);
app.addBootstrapRegistryInitializer(registry -> {
// Register instance
registry.register(ConfigurationService.class,
context -> new ConfigurationService());
// Migrate to ApplicationContext when ready
registry.addCloseListener(event -> {
ConfigurationService service = event.getBootstrapContext()
.get(ConfigurationService.class);
// Register as singleton bean
event.getApplicationContext()
.getBeanFactory()
.registerSingleton("configurationService", service);
});
});
app.run(args);
}
}Register instances conditionally based on environment:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.bootstrap.BootstrapRegistry;
public class MyApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MyApplication.class);
app.addBootstrapRegistryInitializer(registry -> {
// Always register logger
registry.register(Logger.class,
context -> new ConsoleLogger());
// Conditionally register cache based on environment
registry.register(CacheManager.class, context -> {
// Access other bootstrap instances
Logger logger = context.get(Logger.class);
String profile = System.getenv("SPRING_PROFILES_ACTIVE");
if ("production".equals(profile)) {
logger.log("Using distributed cache");
return new RedisCache();
} else {
logger.log("Using in-memory cache");
return new InMemoryCache();
}
});
});
app.run(args);
}
}Problem: Bootstrap instances that depend on each other circularly.
Solution:
// WRONG - circular dependency
registry.register(ServiceA.class, context ->
new ServiceA(context.get(ServiceB.class)));
registry.register(ServiceB.class, context ->
new ServiceB(context.get(ServiceA.class)));
// CORRECT - break the circle
registry.register(SharedConfig.class,
context -> new SharedConfig());
registry.register(ServiceA.class, context ->
new ServiceA(context.get(SharedConfig.class)));
registry.register(ServiceB.class, context ->
new ServiceB(context.get(SharedConfig.class)));Problem: Bootstrap instance not migrated to ApplicationContext, unavailable to beans.
Solution:
registry.register(MyService.class, context -> new MyService());
// Always add close listener to migrate
registry.addCloseListener(event -> {
MyService service = event.getBootstrapContext().get(MyService.class);
event.getApplicationContext()
.getBeanFactory()
.registerSingleton("myService", service);
});Problem: Using SINGLETON for stateful instances that should be per-request.
Solution:
// WRONG - stateful instance as singleton
registry.register(RequestContext.class,
InstanceSupplier.from(() -> new RequestContext()));
// CORRECT - use PROTOTYPE for stateful instances
registry.register(RequestContext.class,
InstanceSupplier.from(() -> new RequestContext())
.withScope(Scope.PROTOTYPE));import org.springframework.boot.bootstrap.BootstrapRegistry;
import org.springframework.boot.bootstrap.BootstrapContext;
import org.springframework.boot.bootstrap.ConfigurableBootstrapContext;
import org.springframework.boot.bootstrap.BootstrapRegistryInitializer;
import org.springframework.boot.bootstrap.BootstrapContextClosedEvent;
import org.springframework.boot.bootstrap.DefaultBootstrapContext;
import org.springframework.boot.bootstrap.BootstrapRegistry.InstanceSupplier;
import org.springframework.boot.bootstrap.BootstrapRegistry.Scope;