diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutionProperties.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutionProperties.java index 144fbb60e67c..306d34630ef8 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutionProperties.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutionProperties.java @@ -44,6 +44,11 @@ public class TaskExecutionProperties { */ private Mode mode = Mode.AUTO; + /** + * Whether to propagate the current context to task executions. + */ + private boolean propagateContext = false; + /** * Prefix to use for the names of newly created threads. */ @@ -69,6 +74,14 @@ public void setMode(Mode mode) { this.mode = mode; } + public boolean getPropagateContext() { + return this.propagateContext; + } + + public void setPropagateContext(boolean propagateContext) { + this.propagateContext = propagateContext; + } + public String getThreadNamePrefix() { return this.threadNamePrefix; } diff --git a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutorConfigurations.java b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutorConfigurations.java index a519c65374a9..351167085692 100644 --- a/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutorConfigurations.java +++ b/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutorConfigurations.java @@ -86,6 +86,11 @@ ThreadPoolTaskExecutor applicationTaskExecutor(ThreadPoolTaskExecutorBuilder thr return threadPoolTaskExecutorBuilder.build(); } + @Bean + @ConditionalOnProperty(prefix = "spring.task.execution.propagate-context", havingValue = "true") + TaskDecorator contextPropagatingTaskDecorator() { + return new ContextPropagatingTaskDecorator(); + } } @Configuration(proxyBeanMethods = false) diff --git a/core/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/task/TaskExecutionAutoConfigurationTests.java b/core/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/task/TaskExecutionAutoConfigurationTests.java index 40d6fb26e7a6..a1ef0b3af064 100644 --- a/core/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/task/TaskExecutionAutoConfigurationTests.java +++ b/core/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/task/TaskExecutionAutoConfigurationTests.java @@ -92,13 +92,15 @@ void simpleAsyncTaskExecutorBuilderShouldReadProperties() { "spring.task.execution.simple.reject-tasks-when-limit-reached=true", "spring.task.execution.simple.concurrency-limit=1", "spring.task.execution.shutdown.await-termination=true", - "spring.task.execution.shutdown.await-termination-period=30s") + "spring.task.execution.shutdown.await-termination-period=30s", + "spring.task.execution.propagate-context=true") .run(assertSimpleAsyncTaskExecutor((taskExecutor) -> { assertThat(taskExecutor).hasFieldOrPropertyWithValue("cancelRemainingTasksOnClose", true); assertThat(taskExecutor).hasFieldOrPropertyWithValue("rejectTasksWhenLimitReached", true); assertThat(taskExecutor.getConcurrencyLimit()).isEqualTo(1); assertThat(taskExecutor.getThreadNamePrefix()).isEqualTo("mytest-"); assertThat(taskExecutor).hasFieldOrPropertyWithValue("taskTerminationTimeout", 30000L); + assertThat(taskExecutor).hasFieldOrPropertyWithValue("propagateContext", true); })); } @@ -253,6 +255,16 @@ void simpleAsyncTaskExecutorBuilderUsesVirtualThreadsWhenEnabled() { }); } + @Test + void asyncTaskExecutorShouldApplyContextPropagatingDecoratorWhenEnabled() { + this.contextRunner.withPropertyValues("spring.task.execution.propagate-context=true") + .run((context) -> { + assertThat(context).hasSingleBean(ContextPropagatingTaskDecorator.class); + assertThat(context.getBean(ContextPropagatingTaskDecorator.class)) + .isSameAs(context.getBean("contextPropagatingTaskDecorator")); + }); + } + @Test void taskExecutorWhenHasCustomTaskExecutorShouldBackOff() { this.contextRunner.withBean("customTaskExecutor", Executor.class, SyncTaskExecutor::new).run((context) -> {