Skip to content

Commit 10b15e3

Browse files
committed
Auto-configure a bootstrapExecutor bean to be used by Framework's background bean initialization
If there's no bean named bootstrapExecutor but there's a bean named applicationTaskExecutor, we create an alias named 'bootstrapExecutor' for the 'applicationTaskExecutor' bean. Closes gh-39791
1 parent 8e89479 commit 10b15e3

File tree

3 files changed

+71
-8
lines changed

3 files changed

+71
-8
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutionAutoConfiguration.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2024 the original author or authors.
2+
* Copyright 2012-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -37,7 +37,8 @@
3737
@EnableConfigurationProperties(TaskExecutionProperties.class)
3838
@Import({ TaskExecutorConfigurations.ThreadPoolTaskExecutorBuilderConfiguration.class,
3939
TaskExecutorConfigurations.SimpleAsyncTaskExecutorBuilderConfiguration.class,
40-
TaskExecutorConfigurations.TaskExecutorConfiguration.class })
40+
TaskExecutorConfigurations.TaskExecutorConfiguration.class,
41+
TaskExecutorConfigurations.BootstrapExecutorConfiguration.class })
4142
public class TaskExecutionAutoConfiguration {
4243

4344
/**

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutorConfigurations.java

+21
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import org.springframework.beans.factory.BeanFactory;
2222
import org.springframework.beans.factory.ObjectProvider;
23+
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
2324
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
2425
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2526
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -162,6 +163,26 @@ public Executor getAsyncExecutor() {
162163

163164
}
164165

166+
@Configuration(proxyBeanMethods = false)
167+
static class BootstrapExecutorConfiguration {
168+
169+
private static final String BOOTSTRAP_EXECUTOR_NAME = "bootstrapExecutor";
170+
171+
@Bean
172+
static BeanFactoryPostProcessor bootstrapExecutorAliasPostProcessor() {
173+
return (beanFactory) -> {
174+
boolean hasBootstrapExecutor = beanFactory.containsBean(BOOTSTRAP_EXECUTOR_NAME);
175+
boolean hasApplicationTaskExecutor = beanFactory
176+
.containsBean(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME);
177+
if (!hasBootstrapExecutor && hasApplicationTaskExecutor) {
178+
beanFactory.registerAlias(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME,
179+
BOOTSTRAP_EXECUTOR_NAME);
180+
}
181+
};
182+
}
183+
184+
}
185+
165186
static class OnExecutorCondition extends AnyNestedCondition {
166187

167188
OnExecutorCondition() {

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/task/TaskExecutionAutoConfigurationTests.java

+47-6
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import java.util.concurrent.CompletableFuture;
2020
import java.util.concurrent.CountDownLatch;
2121
import java.util.concurrent.Executor;
22+
import java.util.concurrent.ExecutorService;
23+
import java.util.concurrent.Executors;
2224
import java.util.concurrent.Future;
2325
import java.util.concurrent.TimeUnit;
2426
import java.util.concurrent.atomic.AtomicReference;
@@ -364,12 +366,6 @@ void enableAsyncUsesAutoConfiguredExecutorWhenModeIsForceAndHasPrimaryCustomTask
364366
});
365367
}
366368

367-
private Executor createCustomAsyncExecutor(String threadNamePrefix) {
368-
SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor();
369-
executor.setThreadNamePrefix(threadNamePrefix);
370-
return executor;
371-
}
372-
373369
@Test
374370
void enableAsyncUsesAutoConfiguredOneByDefaultEvenThoughSchedulingIsConfigured() {
375371
this.contextRunner.withPropertyValues("spring.task.execution.thread-name-prefix=auto-task-")
@@ -382,6 +378,51 @@ void enableAsyncUsesAutoConfiguredOneByDefaultEvenThoughSchedulingIsConfigured()
382378
});
383379
}
384380

381+
@Test
382+
void shouldAliasApplicationExecutorToBootstrapExecutor() {
383+
this.contextRunner.run((context) -> {
384+
String[] aliases = context.getAliases("applicationTaskExecutor");
385+
assertThat(aliases).containsExactly("bootstrapExecutor");
386+
});
387+
}
388+
389+
@Test
390+
void shouldNotAliasIfBootstrapExecutorIsDefined() {
391+
ExecutorService executor = Executors.newSingleThreadExecutor();
392+
try {
393+
this.contextRunner.withBean("applicationTaskExecutor", Executor.class, () -> executor)
394+
.withBean("bootstrapExecutor", Executor.class, () -> executor)
395+
.run((context) -> {
396+
assertThat(context).hasBean("applicationTaskExecutor");
397+
String[] aliases = context.getAliases("applicationTaskExecutor");
398+
assertThat(aliases).isEmpty();
399+
});
400+
}
401+
finally {
402+
executor.shutdownNow();
403+
}
404+
}
405+
406+
@Test
407+
void shouldNotAliasIfApplicationTaskExecutorIsMissing() {
408+
ExecutorService executor = Executors.newSingleThreadExecutor();
409+
try {
410+
this.contextRunner.withBean("customExecutor", Executor.class, () -> executor).run((context) -> {
411+
assertThat(context).doesNotHaveBean("applicationTaskExecutor");
412+
assertThat(context).doesNotHaveBean("bootstrapExecutor");
413+
});
414+
}
415+
finally {
416+
executor.shutdownNow();
417+
}
418+
}
419+
420+
private Executor createCustomAsyncExecutor(String threadNamePrefix) {
421+
SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor();
422+
executor.setThreadNamePrefix(threadNamePrefix);
423+
return executor;
424+
}
425+
385426
private ContextConsumer<AssertableApplicationContext> assertThreadPoolTaskExecutor(
386427
Consumer<ThreadPoolTaskExecutor> taskExecutor) {
387428
return (context) -> {

0 commit comments

Comments
 (0)