Browse Source

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
pull/45161/head
Moritz Halbritter 9 months ago
parent
commit
10b15e301b
  1. 5
      spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutionAutoConfiguration.java
  2. 21
      spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutorConfigurations.java
  3. 53
      spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/task/TaskExecutionAutoConfigurationTests.java

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

@ -1,5 +1,5 @@ @@ -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; @@ -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 {
/**

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

@ -20,6 +20,7 @@ import java.util.concurrent.Executor; @@ -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 { @@ -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() {

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

@ -19,6 +19,8 @@ package org.springframework.boot.autoconfigure.task; @@ -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 { @@ -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 { @@ -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) -> {

Loading…
Cancel
Save