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
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -37,7 +37,8 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
@EnableConfigurationProperties(TaskExecutionProperties.class)
|
||||
@Import({ TaskExecutorConfigurations.ThreadPoolTaskExecutorBuilderConfiguration.class,
|
||||
TaskExecutorConfigurations.SimpleAsyncTaskExecutorBuilderConfiguration.class,
|
||||
TaskExecutorConfigurations.TaskExecutorConfiguration.class })
|
||||
TaskExecutorConfigurations.TaskExecutorConfiguration.class,
|
||||
TaskExecutorConfigurations.BootstrapExecutorConfiguration.class })
|
||||
public class TaskExecutionAutoConfiguration {
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.util.concurrent.Executor;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
@@ -162,6 +163,26 @@ class TaskExecutorConfigurations {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class BootstrapExecutorConfiguration {
|
||||
|
||||
private static final String BOOTSTRAP_EXECUTOR_NAME = "bootstrapExecutor";
|
||||
|
||||
@Bean
|
||||
static BeanFactoryPostProcessor bootstrapExecutorAliasPostProcessor() {
|
||||
return (beanFactory) -> {
|
||||
boolean hasBootstrapExecutor = beanFactory.containsBean(BOOTSTRAP_EXECUTOR_NAME);
|
||||
boolean hasApplicationTaskExecutor = beanFactory
|
||||
.containsBean(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME);
|
||||
if (!hasBootstrapExecutor && hasApplicationTaskExecutor) {
|
||||
beanFactory.registerAlias(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME,
|
||||
BOOTSTRAP_EXECUTOR_NAME);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class OnExecutorCondition extends AnyNestedCondition {
|
||||
|
||||
OnExecutorCondition() {
|
||||
|
||||
@@ -19,6 +19,8 @@ package org.springframework.boot.autoconfigure.task;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
@@ -364,12 +366,6 @@ class TaskExecutionAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
private Executor createCustomAsyncExecutor(String threadNamePrefix) {
|
||||
SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor();
|
||||
executor.setThreadNamePrefix(threadNamePrefix);
|
||||
return executor;
|
||||
}
|
||||
|
||||
@Test
|
||||
void enableAsyncUsesAutoConfiguredOneByDefaultEvenThoughSchedulingIsConfigured() {
|
||||
this.contextRunner.withPropertyValues("spring.task.execution.thread-name-prefix=auto-task-")
|
||||
@@ -382,6 +378,51 @@ class TaskExecutionAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAliasApplicationExecutorToBootstrapExecutor() {
|
||||
this.contextRunner.run((context) -> {
|
||||
String[] aliases = context.getAliases("applicationTaskExecutor");
|
||||
assertThat(aliases).containsExactly("bootstrapExecutor");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotAliasIfBootstrapExecutorIsDefined() {
|
||||
ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
try {
|
||||
this.contextRunner.withBean("applicationTaskExecutor", Executor.class, () -> executor)
|
||||
.withBean("bootstrapExecutor", Executor.class, () -> executor)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasBean("applicationTaskExecutor");
|
||||
String[] aliases = context.getAliases("applicationTaskExecutor");
|
||||
assertThat(aliases).isEmpty();
|
||||
});
|
||||
}
|
||||
finally {
|
||||
executor.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotAliasIfApplicationTaskExecutorIsMissing() {
|
||||
ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
try {
|
||||
this.contextRunner.withBean("customExecutor", Executor.class, () -> executor).run((context) -> {
|
||||
assertThat(context).doesNotHaveBean("applicationTaskExecutor");
|
||||
assertThat(context).doesNotHaveBean("bootstrapExecutor");
|
||||
});
|
||||
}
|
||||
finally {
|
||||
executor.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
private Executor createCustomAsyncExecutor(String threadNamePrefix) {
|
||||
SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor();
|
||||
executor.setThreadNamePrefix(threadNamePrefix);
|
||||
return executor;
|
||||
}
|
||||
|
||||
private ContextConsumer<AssertableApplicationContext> assertThreadPoolTaskExecutor(
|
||||
Consumer<ThreadPoolTaskExecutor> taskExecutor) {
|
||||
return (context) -> {
|
||||
|
||||
Reference in New Issue
Block a user