Speed Up Spring Startup with Parallel Bean Initialization
This article introduces Spring 6.2’s Parallel Bean Initialization feature, explains its benefits for reducing startup time, provides step‑by‑step code examples for configuring background bean initialization and a bootstrap executor, compares traditional and concurrent initialization timings, and details the underlying implementation in the Spring container.
1. Introduction
Spring 6.2.0‑SNAPSHOT adds a new feature called Parallel Bean Initialization during startup, which can significantly reduce the time it takes for a Spring application to start by initializing multiple beans concurrently while preserving dependency relationships.
2. Practical Example
2.1 Environment Setup
static class PersonService {
public PersonService() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {}
System.out.println("PersonService init ...");
}
}
static class CommonService {
public CommonService() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {}
System.out.println("CommonService init ...");
}
}2.2 Traditional Bean Initialization
@Configuration
static class AppConfig {
@Bean
public PersonService personService() {
return new PersonService();
}
@Bean
public CommonService commonService() {
return new CommonService();
}
} StopWatch watch = new StopWatch("初始化容器");
watch.start();
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class)) {
}
watch.stop();
System.out.println(watch.prettyPrint());Output:
PersonService init ...
CommonService init ...
StopWatch '初始化容器': 6.3145531 seconds
----------------------------------------
Seconds % Task name
----------------------------------------
6.3145531 100%Total time: 6.3 seconds.
2.3 Concurrent Bean Initialization
Modify the @Bean definitions to run in the background:
// Set bean initialization to run in the background
@Bean(bootstrap = Bootstrap.BACKGROUND)
public PersonService personService() {
return new PersonService();
}
@Bean(bootstrap = Bootstrap.BACKGROUND)
public CommonService commonService() {
return new CommonService();
}Run the test again and observe the reduced time.
The log indicates that commonService is marked for background initialization but no bootstrap executor is configured, so we need to define one:
@Bean
public Executor bootstrapExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(5);
taskExecutor.setMaxPoolSize(5);
taskExecutor.initialize();
return taskExecutor;
}After adding the executor, the output becomes:
CommonService init ...
PersonService init ...
StopWatch '初始化容器': 3.3203919 seconds
----------------------------------------
Seconds % Task name
----------------------------------------
3.3203919 100%Total time: 3.3 seconds, showing that the beans were initialized on separate threads.
If beans are defined via BeanDefinition instead of annotations, the background flag must be set manually:
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
context.register(AppConfig.class);
context.registerBean(PersonDAO.class, bd -> {
if (bd instanceof AnnotatedGenericBeanDefinition bean) {
bean.setBackgroundInit(true);
}
});
context.refresh();
}3. Implementation Details
The Spring container’s refresh() method eventually calls finishBeanFactoryInitialization, which looks for a bean named bootstrapExecutor of type Executor and stores it for later use.
public abstract class AbstractApplicationContext {
public void refresh() {
finishBeanFactoryInitialization(beanFactory);
}
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// initialize bootstrap executor
if (beanFactory.containsBean(BOOTSTRAP_EXECUTOR_BEAN_NAME) &&
beanFactory.isTypeMatch(BOOTSTRAP_EXECUTOR_BEAN_NAME, Executor.class)) {
beanFactory.setBootstrapExecutor(
beanFactory.getBean(BOOTSTRAP_EXECUTOR_BEAN_NAME, Executor.class)
);
}
// instantiate non‑lazy singleton beans
beanFactory.preInstantiateSingletons();
}
}The DefaultListableBeanFactory creates a list of CompletableFuture objects for beans that support background initialization, runs them with the configured executor, and waits for all futures to complete before proceeding.
public class DefaultListableBeanFactory {
public void preInstantiateSingletons() {
List<CompletableFuture<?>> futures = new ArrayList<>();
for (String beanName : beanNames) {
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
if (!mbd.isAbstract() && mbd.isSingleton()) {
CompletableFuture<?> future = preInstantiateSingleton(beanName, mbd);
if (future != null) {
futures.add(future);
}
}
}
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
}
private CompletableFuture<?> preInstantiateSingleton(String beanName, RootBeanDefinition mbd) {
if (mbd.isBackgroundInit()) {
Executor executor = getBootstrapExecutor();
if (executor != null) {
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
getBean(dep);
}
}
CompletableFuture<?> future = CompletableFuture.runAsync(
() -> instantiateSingletonInBackgroundThread(beanName), executor);
addSingletonFactory(beanName, () -> {
try { future.join(); } catch (Exception e) { }
return future;
});
return (!mbd.isLazyInit() ? future : null);
}
}
if (!mbd.isLazyInit()) {
instantiateSingleton(beanName);
}
return null;
}
}The overall mechanism is straightforward: beans marked for background initialization are handed off to a thread pool, while beans without that flag are created on the main thread.
That concludes the article. If you found it helpful, consider treating the author to a lollipop 🍭.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
