From f0c5312141d05bfcff2e0de9eb5da36127c2183a Mon Sep 17 00:00:00 2001 From: Dmytro Nosan Date: Fri, 15 Nov 2024 18:07:50 +0200 Subject: [PATCH 1/2] Add TaskDecorator support for scheduled tasks See gh-43190 --- .../task/TaskSchedulingConfigurations.java | 12 +++- .../TaskSchedulingAutoConfigurationTests.java | 36 +++++++++++ .../task/SimpleAsyncTaskSchedulerBuilder.java | 38 +++++++++--- .../task/ThreadPoolTaskSchedulerBuilder.java | 62 +++++++++++++++---- .../SimpleAsyncTaskSchedulerBuilderTests.java | 10 ++- .../ThreadPoolTaskSchedulerBuilderTests.java | 10 ++- 6 files changed, 144 insertions(+), 24 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskSchedulingConfigurations.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskSchedulingConfigurations.java index 0112171fa7..b0cb0d187c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskSchedulingConfigurations.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskSchedulingConfigurations.java @@ -29,6 +29,7 @@ import org.springframework.boot.task.ThreadPoolTaskSchedulerBuilder; import org.springframework.boot.task.ThreadPoolTaskSchedulerCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.task.TaskDecorator; import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.concurrent.SimpleAsyncTaskScheduler; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; @@ -67,7 +68,8 @@ class TaskSchedulingConfigurations { @Bean @ConditionalOnMissingBean ThreadPoolTaskSchedulerBuilder threadPoolTaskSchedulerBuilder(TaskSchedulingProperties properties, - ObjectProvider threadPoolTaskSchedulerCustomizers) { + ObjectProvider threadPoolTaskSchedulerCustomizers, + ObjectProvider taskDecorator) { TaskSchedulingProperties.Shutdown shutdown = properties.getShutdown(); ThreadPoolTaskSchedulerBuilder builder = new ThreadPoolTaskSchedulerBuilder(); builder = builder.poolSize(properties.getPool().getSize()); @@ -75,6 +77,7 @@ class TaskSchedulingConfigurations { builder = builder.awaitTerminationPeriod(shutdown.getAwaitTerminationPeriod()); builder = builder.threadNamePrefix(properties.getThreadNamePrefix()); builder = builder.customizers(threadPoolTaskSchedulerCustomizers); + builder = builder.taskDecorator(taskDecorator.getIfUnique()); return builder; } @@ -87,10 +90,14 @@ class TaskSchedulingConfigurations { private final ObjectProvider taskSchedulerCustomizers; + private final ObjectProvider taskDecorator; + SimpleAsyncTaskSchedulerBuilderConfiguration(TaskSchedulingProperties properties, - ObjectProvider taskSchedulerCustomizers) { + ObjectProvider taskSchedulerCustomizers, + ObjectProvider taskDecorator) { this.properties = properties; this.taskSchedulerCustomizers = taskSchedulerCustomizers; + this.taskDecorator = taskDecorator; } @Bean @@ -117,6 +124,7 @@ class TaskSchedulingConfigurations { if (shutdown.isAwaitTermination()) { builder = builder.taskTerminationTimeout(shutdown.getAwaitTerminationPeriod()); } + builder = builder.taskDecorator(this.taskDecorator.getIfUnique()); return builder; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/task/TaskSchedulingAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/task/TaskSchedulingAutoConfigurationTests.java index 74dc49d974..62963d3832 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/task/TaskSchedulingAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/task/TaskSchedulingAutoConfigurationTests.java @@ -41,6 +41,7 @@ import org.springframework.boot.task.ThreadPoolTaskSchedulerCustomizer; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.task.TaskDecorator; import org.springframework.core.task.TaskExecutor; import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.annotation.EnableScheduling; @@ -50,6 +51,7 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.scheduling.config.ScheduledTaskRegistrar; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; /** * Tests for {@link TaskSchedulingAutoConfiguration}. @@ -154,6 +156,30 @@ class TaskSchedulingAutoConfigurationTests { }); } + @Test + void simpleAsyncTaskSchedulerBuilderShouldApplyTaskDecorator() { + this.contextRunner.withUserConfiguration(SchedulingConfiguration.class, TaskDecoratorConfig.class) + .run((context) -> { + assertThat(context).hasSingleBean(SimpleAsyncTaskSchedulerBuilder.class); + assertThat(context).hasSingleBean(TaskDecorator.class); + TaskDecorator taskDecorator = context.getBean(TaskDecorator.class); + SimpleAsyncTaskSchedulerBuilder builder = context.getBean(SimpleAsyncTaskSchedulerBuilder.class); + assertThat(builder).extracting("taskDecorator").isSameAs(taskDecorator); + }); + } + + @Test + void threadPoolTaskSchedulerBuilderShouldApplyTaskDecorator() { + this.contextRunner.withUserConfiguration(SchedulingConfiguration.class, TaskDecoratorConfig.class) + .run((context) -> { + assertThat(context).hasSingleBean(ThreadPoolTaskSchedulerBuilder.class); + assertThat(context).hasSingleBean(TaskDecorator.class); + TaskDecorator taskDecorator = context.getBean(TaskDecorator.class); + ThreadPoolTaskSchedulerBuilder builder = context.getBean(ThreadPoolTaskSchedulerBuilder.class); + assertThat(builder).extracting("taskDecorator").isSameAs(taskDecorator); + }); + } + @Test void enableSchedulingWithNoTaskExecutorAppliesCustomizers() { this.contextRunner.withPropertyValues("spring.task.scheduling.thread-name-prefix=scheduling-test-") @@ -305,4 +331,14 @@ class TaskSchedulingAutoConfigurationTests { } + @Configuration(proxyBeanMethods = false) + static class TaskDecoratorConfig { + + @Bean + TaskDecorator mockTaskDecorator() { + return mock(TaskDecorator.class); + } + + } + } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/task/SimpleAsyncTaskSchedulerBuilder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/task/SimpleAsyncTaskSchedulerBuilder.java index bdd40662df..4cd498a65e 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/task/SimpleAsyncTaskSchedulerBuilder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/task/SimpleAsyncTaskSchedulerBuilder.java @@ -23,6 +23,7 @@ import java.util.LinkedHashSet; import java.util.Set; import org.springframework.boot.context.properties.PropertyMapper; +import org.springframework.core.task.TaskDecorator; import org.springframework.scheduling.concurrent.SimpleAsyncTaskScheduler; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; @@ -49,18 +50,27 @@ public class SimpleAsyncTaskSchedulerBuilder { private final Duration taskTerminationTimeout; + private final TaskDecorator taskDecorator; + private final Set customizers; + /** + * Constructs a new {@code SimpleAsyncTaskSchedulerBuilder} with default settings. + * Initializes a builder instance with all fields set to {@code null}, allowing for + * further customization through its fluent API methods. + */ public SimpleAsyncTaskSchedulerBuilder() { - this(null, null, null, null, null); + this(null, null, null, null, null, null); } private SimpleAsyncTaskSchedulerBuilder(String threadNamePrefix, Integer concurrencyLimit, Boolean virtualThreads, - Duration taskTerminationTimeout, Set taskSchedulerCustomizers) { + Duration taskTerminationTimeout, TaskDecorator taskDecorator, + Set taskSchedulerCustomizers) { this.threadNamePrefix = threadNamePrefix; this.concurrencyLimit = concurrencyLimit; this.virtualThreads = virtualThreads; this.customizers = taskSchedulerCustomizers; + this.taskDecorator = taskDecorator; this.taskTerminationTimeout = taskTerminationTimeout; } @@ -71,7 +81,7 @@ public class SimpleAsyncTaskSchedulerBuilder { */ public SimpleAsyncTaskSchedulerBuilder threadNamePrefix(String threadNamePrefix) { return new SimpleAsyncTaskSchedulerBuilder(threadNamePrefix, this.concurrencyLimit, this.virtualThreads, - this.taskTerminationTimeout, this.customizers); + this.taskTerminationTimeout, this.taskDecorator, this.customizers); } /** @@ -81,7 +91,7 @@ public class SimpleAsyncTaskSchedulerBuilder { */ public SimpleAsyncTaskSchedulerBuilder concurrencyLimit(Integer concurrencyLimit) { return new SimpleAsyncTaskSchedulerBuilder(this.threadNamePrefix, concurrencyLimit, this.virtualThreads, - this.taskTerminationTimeout, this.customizers); + this.taskTerminationTimeout, this.taskDecorator, this.customizers); } /** @@ -91,7 +101,7 @@ public class SimpleAsyncTaskSchedulerBuilder { */ public SimpleAsyncTaskSchedulerBuilder virtualThreads(Boolean virtualThreads) { return new SimpleAsyncTaskSchedulerBuilder(this.threadNamePrefix, this.concurrencyLimit, virtualThreads, - this.taskTerminationTimeout, this.customizers); + this.taskTerminationTimeout, this.taskDecorator, this.customizers); } /** @@ -102,7 +112,7 @@ public class SimpleAsyncTaskSchedulerBuilder { */ public SimpleAsyncTaskSchedulerBuilder taskTerminationTimeout(Duration taskTerminationTimeout) { return new SimpleAsyncTaskSchedulerBuilder(this.threadNamePrefix, this.concurrencyLimit, this.virtualThreads, - taskTerminationTimeout, this.customizers); + taskTerminationTimeout, this.taskDecorator, this.customizers); } /** @@ -132,7 +142,7 @@ public class SimpleAsyncTaskSchedulerBuilder { Iterable customizers) { Assert.notNull(customizers, "Customizers must not be null"); return new SimpleAsyncTaskSchedulerBuilder(this.threadNamePrefix, this.concurrencyLimit, this.virtualThreads, - this.taskTerminationTimeout, append(null, customizers)); + this.taskTerminationTimeout, this.taskDecorator, append(null, customizers)); } /** @@ -160,7 +170,18 @@ public class SimpleAsyncTaskSchedulerBuilder { Iterable customizers) { Assert.notNull(customizers, "Customizers must not be null"); return new SimpleAsyncTaskSchedulerBuilder(this.threadNamePrefix, this.concurrencyLimit, this.virtualThreads, - this.taskTerminationTimeout, append(this.customizers, customizers)); + this.taskTerminationTimeout, this.taskDecorator, append(this.customizers, customizers)); + } + + /** + * Set the task decorator to be used by the {@link SimpleAsyncTaskScheduler}. + * @param taskDecorator the task decorator to set + * @return a new builder instance + * @since 3.5.0 + */ + public SimpleAsyncTaskSchedulerBuilder taskDecorator(TaskDecorator taskDecorator) { + return new SimpleAsyncTaskSchedulerBuilder(this.threadNamePrefix, this.concurrencyLimit, this.virtualThreads, + this.taskTerminationTimeout, taskDecorator, this.customizers); } /** @@ -187,6 +208,7 @@ public class SimpleAsyncTaskSchedulerBuilder { map.from(this.concurrencyLimit).to(taskScheduler::setConcurrencyLimit); map.from(this.virtualThreads).to(taskScheduler::setVirtualThreads); map.from(this.taskTerminationTimeout).as(Duration::toMillis).to(taskScheduler::setTaskTerminationTimeout); + map.from(this.taskDecorator).to(taskScheduler::setTaskDecorator); if (!CollectionUtils.isEmpty(this.customizers)) { this.customizers.forEach((customizer) -> customizer.customize(taskScheduler)); } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/task/ThreadPoolTaskSchedulerBuilder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/task/ThreadPoolTaskSchedulerBuilder.java index a36e48308e..9815554056 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/task/ThreadPoolTaskSchedulerBuilder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/task/ThreadPoolTaskSchedulerBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -23,6 +23,7 @@ import java.util.LinkedHashSet; import java.util.Set; import org.springframework.boot.context.properties.PropertyMapper; +import org.springframework.core.task.TaskDecorator; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; @@ -48,23 +49,48 @@ public class ThreadPoolTaskSchedulerBuilder { private final String threadNamePrefix; + private final TaskDecorator taskDecorator; + private final Set customizers; + /** + * Default constructor for creating a new instance of + * {@code ThreadPoolTaskSchedulerBuilder}. Initializes a builder instance with all + * fields set to {@code null}, allowing for further customization through its fluent + * API methods. + */ public ThreadPoolTaskSchedulerBuilder() { - this.poolSize = null; - this.awaitTermination = null; - this.awaitTerminationPeriod = null; - this.threadNamePrefix = null; - this.customizers = null; + this(null, null, null, null, null, null); } + /** + * Constructs a new {@code ThreadPoolTaskSchedulerBuilder} instance with the specified + * configuration. + * @param poolSize the maximum allowed number of threads + * @param awaitTermination whether the executor should wait for scheduled tasks to + * complete on shutdown + * @param awaitTerminationPeriod the maximum time the executor is supposed to block on + * shutdown + * @param threadNamePrefix the prefix to use for the names of newly created threads + * @param taskSchedulerCustomizers the customizers to apply to the + * {@link ThreadPoolTaskScheduler} + * @deprecated since 3.5.0 for removal in 3.7.0 in favor of the default constructor + */ + @Deprecated(since = "3.5.0", forRemoval = true) public ThreadPoolTaskSchedulerBuilder(Integer poolSize, Boolean awaitTermination, Duration awaitTerminationPeriod, String threadNamePrefix, Set taskSchedulerCustomizers) { + this(poolSize, awaitTermination, awaitTerminationPeriod, threadNamePrefix, taskSchedulerCustomizers, null); + } + + private ThreadPoolTaskSchedulerBuilder(Integer poolSize, Boolean awaitTermination, Duration awaitTerminationPeriod, + String threadNamePrefix, Set taskSchedulerCustomizers, + TaskDecorator taskDecorator) { this.poolSize = poolSize; this.awaitTermination = awaitTermination; this.awaitTerminationPeriod = awaitTerminationPeriod; this.threadNamePrefix = threadNamePrefix; this.customizers = taskSchedulerCustomizers; + this.taskDecorator = taskDecorator; } /** @@ -74,7 +100,7 @@ public class ThreadPoolTaskSchedulerBuilder { */ public ThreadPoolTaskSchedulerBuilder poolSize(int poolSize) { return new ThreadPoolTaskSchedulerBuilder(poolSize, this.awaitTermination, this.awaitTerminationPeriod, - this.threadNamePrefix, this.customizers); + this.threadNamePrefix, this.customizers, this.taskDecorator); } /** @@ -87,7 +113,7 @@ public class ThreadPoolTaskSchedulerBuilder { */ public ThreadPoolTaskSchedulerBuilder awaitTermination(boolean awaitTermination) { return new ThreadPoolTaskSchedulerBuilder(this.poolSize, awaitTermination, this.awaitTerminationPeriod, - this.threadNamePrefix, this.customizers); + this.threadNamePrefix, this.customizers, this.taskDecorator); } /** @@ -101,7 +127,7 @@ public class ThreadPoolTaskSchedulerBuilder { */ public ThreadPoolTaskSchedulerBuilder awaitTerminationPeriod(Duration awaitTerminationPeriod) { return new ThreadPoolTaskSchedulerBuilder(this.poolSize, this.awaitTermination, awaitTerminationPeriod, - this.threadNamePrefix, this.customizers); + this.threadNamePrefix, this.customizers, this.taskDecorator); } /** @@ -111,7 +137,18 @@ public class ThreadPoolTaskSchedulerBuilder { */ public ThreadPoolTaskSchedulerBuilder threadNamePrefix(String threadNamePrefix) { return new ThreadPoolTaskSchedulerBuilder(this.poolSize, this.awaitTermination, this.awaitTerminationPeriod, - threadNamePrefix, this.customizers); + threadNamePrefix, this.customizers, this.taskDecorator); + } + + /** + * Set the {@link TaskDecorator} to be applied to the {@link ThreadPoolTaskScheduler}. + * @param taskDecorator the task decorator to set + * @return a new builder instance + * @since 3.5.0 + */ + public ThreadPoolTaskSchedulerBuilder taskDecorator(TaskDecorator taskDecorator) { + return new ThreadPoolTaskSchedulerBuilder(this.poolSize, this.awaitTermination, this.awaitTerminationPeriod, + this.threadNamePrefix, this.customizers, taskDecorator); } /** @@ -143,7 +180,7 @@ public class ThreadPoolTaskSchedulerBuilder { Iterable customizers) { Assert.notNull(customizers, "Customizers must not be null"); return new ThreadPoolTaskSchedulerBuilder(this.poolSize, this.awaitTermination, this.awaitTerminationPeriod, - this.threadNamePrefix, append(null, customizers)); + this.threadNamePrefix, append(null, customizers), this.taskDecorator); } /** @@ -173,7 +210,7 @@ public class ThreadPoolTaskSchedulerBuilder { Iterable customizers) { Assert.notNull(customizers, "Customizers must not be null"); return new ThreadPoolTaskSchedulerBuilder(this.poolSize, this.awaitTermination, this.awaitTerminationPeriod, - this.threadNamePrefix, append(this.customizers, customizers)); + this.threadNamePrefix, append(this.customizers, customizers), this.taskDecorator); } /** @@ -199,6 +236,7 @@ public class ThreadPoolTaskSchedulerBuilder { map.from(this.awaitTermination).to(taskScheduler::setWaitForTasksToCompleteOnShutdown); map.from(this.awaitTerminationPeriod).asInt(Duration::getSeconds).to(taskScheduler::setAwaitTerminationSeconds); map.from(this.threadNamePrefix).to(taskScheduler::setThreadNamePrefix); + map.from(this.taskDecorator).to(taskScheduler::setTaskDecorator); if (!CollectionUtils.isEmpty(this.customizers)) { this.customizers.forEach((customizer) -> customizer.customize(taskScheduler)); } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/task/SimpleAsyncTaskSchedulerBuilderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/task/SimpleAsyncTaskSchedulerBuilderTests.java index 9cb06c5f32..f2ac75112a 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/task/SimpleAsyncTaskSchedulerBuilderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/task/SimpleAsyncTaskSchedulerBuilderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -24,6 +24,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledForJreRange; import org.junit.jupiter.api.condition.JRE; +import org.springframework.core.task.TaskDecorator; import org.springframework.scheduling.concurrent.SimpleAsyncTaskScheduler; import static org.assertj.core.api.Assertions.assertThat; @@ -134,4 +135,11 @@ class SimpleAsyncTaskSchedulerBuilderTests { assertThat(scheduler).extracting("taskTerminationTimeout").isEqualTo(1000L); } + @Test + void taskDecoratorShouldApply() { + TaskDecorator taskDecorator = mock(TaskDecorator.class); + SimpleAsyncTaskScheduler scheduler = this.builder.taskDecorator(taskDecorator).build(); + assertThat(scheduler).extracting("taskDecorator").isSameAs(taskDecorator); + } + } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/task/ThreadPoolTaskSchedulerBuilderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/task/ThreadPoolTaskSchedulerBuilderTests.java index 11b4f15f49..9411700bfb 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/task/ThreadPoolTaskSchedulerBuilderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/task/ThreadPoolTaskSchedulerBuilderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -22,6 +22,7 @@ import java.util.Set; import org.junit.jupiter.api.Test; +import org.springframework.core.task.TaskDecorator; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import static org.assertj.core.api.Assertions.assertThat; @@ -131,4 +132,11 @@ class ThreadPoolTaskSchedulerBuilderTests { then(customizer2).should().customize(scheduler); } + @Test + void taskDecoratorShouldApply() { + TaskDecorator taskDecorator = mock(TaskDecorator.class); + ThreadPoolTaskScheduler scheduler = this.builder.taskDecorator(taskDecorator).build(); + assertThat(scheduler).extracting("taskDecorator").isSameAs(taskDecorator); + } + } From ced7c1617cfa2e933e67efa2dc839d1d16b15e86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Mon, 18 Nov 2024 14:21:06 +0100 Subject: [PATCH 2/2] Polish "Add TaskDecorator support for scheduled tasks" See gh-43190 --- .../task/TaskSchedulingConfigurations.java | 18 +++++------ .../TaskSchedulingAutoConfigurationTests.java | 30 +++++++++---------- .../task/SimpleAsyncTaskSchedulerBuilder.java | 27 +++++++---------- .../task/ThreadPoolTaskSchedulerBuilder.java | 28 +++++++---------- .../SimpleAsyncTaskSchedulerBuilderTests.java | 26 ++++++++-------- .../ThreadPoolTaskSchedulerBuilderTests.java | 14 ++++----- 6 files changed, 66 insertions(+), 77 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskSchedulingConfigurations.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskSchedulingConfigurations.java index b0cb0d187c..dfe9c59771 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskSchedulingConfigurations.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskSchedulingConfigurations.java @@ -68,16 +68,16 @@ class TaskSchedulingConfigurations { @Bean @ConditionalOnMissingBean ThreadPoolTaskSchedulerBuilder threadPoolTaskSchedulerBuilder(TaskSchedulingProperties properties, - ObjectProvider threadPoolTaskSchedulerCustomizers, - ObjectProvider taskDecorator) { + ObjectProvider taskDecorator, + ObjectProvider threadPoolTaskSchedulerCustomizers) { TaskSchedulingProperties.Shutdown shutdown = properties.getShutdown(); ThreadPoolTaskSchedulerBuilder builder = new ThreadPoolTaskSchedulerBuilder(); builder = builder.poolSize(properties.getPool().getSize()); builder = builder.awaitTermination(shutdown.isAwaitTermination()); builder = builder.awaitTerminationPeriod(shutdown.getAwaitTerminationPeriod()); builder = builder.threadNamePrefix(properties.getThreadNamePrefix()); - builder = builder.customizers(threadPoolTaskSchedulerCustomizers); builder = builder.taskDecorator(taskDecorator.getIfUnique()); + builder = builder.customizers(threadPoolTaskSchedulerCustomizers); return builder; } @@ -88,16 +88,16 @@ class TaskSchedulingConfigurations { private final TaskSchedulingProperties properties; - private final ObjectProvider taskSchedulerCustomizers; - private final ObjectProvider taskDecorator; + private final ObjectProvider taskSchedulerCustomizers; + SimpleAsyncTaskSchedulerBuilderConfiguration(TaskSchedulingProperties properties, - ObjectProvider taskSchedulerCustomizers, - ObjectProvider taskDecorator) { + ObjectProvider taskDecorator, + ObjectProvider taskSchedulerCustomizers) { this.properties = properties; - this.taskSchedulerCustomizers = taskSchedulerCustomizers; this.taskDecorator = taskDecorator; + this.taskSchedulerCustomizers = taskSchedulerCustomizers; } @Bean @@ -117,6 +117,7 @@ class TaskSchedulingConfigurations { private SimpleAsyncTaskSchedulerBuilder builder() { SimpleAsyncTaskSchedulerBuilder builder = new SimpleAsyncTaskSchedulerBuilder(); builder = builder.threadNamePrefix(this.properties.getThreadNamePrefix()); + builder = builder.taskDecorator(this.taskDecorator.getIfUnique()); builder = builder.customizers(this.taskSchedulerCustomizers.orderedStream()::iterator); TaskSchedulingProperties.Simple simple = this.properties.getSimple(); builder = builder.concurrencyLimit(simple.getConcurrencyLimit()); @@ -124,7 +125,6 @@ class TaskSchedulingConfigurations { if (shutdown.isAwaitTermination()) { builder = builder.taskTerminationTimeout(shutdown.getAwaitTerminationPeriod()); } - builder = builder.taskDecorator(this.taskDecorator.getIfUnique()); return builder; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/task/TaskSchedulingAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/task/TaskSchedulingAutoConfigurationTests.java index 62963d3832..b7ae6bb001 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/task/TaskSchedulingAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/task/TaskSchedulingAutoConfigurationTests.java @@ -141,21 +141,6 @@ class TaskSchedulingAutoConfigurationTests { }); } - @Test - void simpleAsyncTaskSchedulerBuilderShouldApplyCustomizers() { - SimpleAsyncTaskSchedulerCustomizer customizer = (scheduler) -> { - }; - this.contextRunner.withBean(SimpleAsyncTaskSchedulerCustomizer.class, () -> customizer) - .withUserConfiguration(SchedulingConfiguration.class) - .run((context) -> { - assertThat(context).hasSingleBean(SimpleAsyncTaskSchedulerBuilder.class); - SimpleAsyncTaskSchedulerBuilder builder = context.getBean(SimpleAsyncTaskSchedulerBuilder.class); - assertThat(builder).extracting("customizers") - .asInstanceOf(InstanceOfAssertFactories.collection(SimpleAsyncTaskSchedulerCustomizer.class)) - .containsExactly(customizer); - }); - } - @Test void simpleAsyncTaskSchedulerBuilderShouldApplyTaskDecorator() { this.contextRunner.withUserConfiguration(SchedulingConfiguration.class, TaskDecoratorConfig.class) @@ -180,6 +165,21 @@ class TaskSchedulingAutoConfigurationTests { }); } + @Test + void simpleAsyncTaskSchedulerBuilderShouldApplyCustomizers() { + SimpleAsyncTaskSchedulerCustomizer customizer = (scheduler) -> { + }; + this.contextRunner.withBean(SimpleAsyncTaskSchedulerCustomizer.class, () -> customizer) + .withUserConfiguration(SchedulingConfiguration.class) + .run((context) -> { + assertThat(context).hasSingleBean(SimpleAsyncTaskSchedulerBuilder.class); + SimpleAsyncTaskSchedulerBuilder builder = context.getBean(SimpleAsyncTaskSchedulerBuilder.class); + assertThat(builder).extracting("customizers") + .asInstanceOf(InstanceOfAssertFactories.collection(SimpleAsyncTaskSchedulerCustomizer.class)) + .containsExactly(customizer); + }); + } + @Test void enableSchedulingWithNoTaskExecutorAppliesCustomizers() { this.contextRunner.withPropertyValues("spring.task.scheduling.thread-name-prefix=scheduling-test-") diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/task/SimpleAsyncTaskSchedulerBuilder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/task/SimpleAsyncTaskSchedulerBuilder.java index 4cd498a65e..24a537f36d 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/task/SimpleAsyncTaskSchedulerBuilder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/task/SimpleAsyncTaskSchedulerBuilder.java @@ -54,11 +54,6 @@ public class SimpleAsyncTaskSchedulerBuilder { private final Set customizers; - /** - * Constructs a new {@code SimpleAsyncTaskSchedulerBuilder} with default settings. - * Initializes a builder instance with all fields set to {@code null}, allowing for - * further customization through its fluent API methods. - */ public SimpleAsyncTaskSchedulerBuilder() { this(null, null, null, null, null, null); } @@ -115,6 +110,17 @@ public class SimpleAsyncTaskSchedulerBuilder { taskTerminationTimeout, this.taskDecorator, this.customizers); } + /** + * Set the task decorator to be used by the {@link SimpleAsyncTaskScheduler}. + * @param taskDecorator the task decorator to set + * @return a new builder instance + * @since 3.5.0 + */ + public SimpleAsyncTaskSchedulerBuilder taskDecorator(TaskDecorator taskDecorator) { + return new SimpleAsyncTaskSchedulerBuilder(this.threadNamePrefix, this.concurrencyLimit, this.virtualThreads, + this.taskTerminationTimeout, taskDecorator, this.customizers); + } + /** * Set the {@link SimpleAsyncTaskSchedulerCustomizer customizers} that should be * applied to the {@link SimpleAsyncTaskScheduler}. Customizers are applied in the @@ -173,17 +179,6 @@ public class SimpleAsyncTaskSchedulerBuilder { this.taskTerminationTimeout, this.taskDecorator, append(this.customizers, customizers)); } - /** - * Set the task decorator to be used by the {@link SimpleAsyncTaskScheduler}. - * @param taskDecorator the task decorator to set - * @return a new builder instance - * @since 3.5.0 - */ - public SimpleAsyncTaskSchedulerBuilder taskDecorator(TaskDecorator taskDecorator) { - return new SimpleAsyncTaskSchedulerBuilder(this.threadNamePrefix, this.concurrencyLimit, this.virtualThreads, - this.taskTerminationTimeout, taskDecorator, this.customizers); - } - /** * Build a new {@link SimpleAsyncTaskScheduler} instance and configure it using this * builder. diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/task/ThreadPoolTaskSchedulerBuilder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/task/ThreadPoolTaskSchedulerBuilder.java index 9815554056..86ffef67c8 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/task/ThreadPoolTaskSchedulerBuilder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/task/ThreadPoolTaskSchedulerBuilder.java @@ -53,12 +53,6 @@ public class ThreadPoolTaskSchedulerBuilder { private final Set customizers; - /** - * Default constructor for creating a new instance of - * {@code ThreadPoolTaskSchedulerBuilder}. Initializes a builder instance with all - * fields set to {@code null}, allowing for further customization through its fluent - * API methods. - */ public ThreadPoolTaskSchedulerBuilder() { this(null, null, null, null, null, null); } @@ -79,18 +73,18 @@ public class ThreadPoolTaskSchedulerBuilder { @Deprecated(since = "3.5.0", forRemoval = true) public ThreadPoolTaskSchedulerBuilder(Integer poolSize, Boolean awaitTermination, Duration awaitTerminationPeriod, String threadNamePrefix, Set taskSchedulerCustomizers) { - this(poolSize, awaitTermination, awaitTerminationPeriod, threadNamePrefix, taskSchedulerCustomizers, null); + this(poolSize, awaitTermination, awaitTerminationPeriod, threadNamePrefix, null, taskSchedulerCustomizers); } private ThreadPoolTaskSchedulerBuilder(Integer poolSize, Boolean awaitTermination, Duration awaitTerminationPeriod, - String threadNamePrefix, Set taskSchedulerCustomizers, - TaskDecorator taskDecorator) { + String threadNamePrefix, TaskDecorator taskDecorator, + Set taskSchedulerCustomizers) { this.poolSize = poolSize; this.awaitTermination = awaitTermination; this.awaitTerminationPeriod = awaitTerminationPeriod; this.threadNamePrefix = threadNamePrefix; - this.customizers = taskSchedulerCustomizers; this.taskDecorator = taskDecorator; + this.customizers = taskSchedulerCustomizers; } /** @@ -100,7 +94,7 @@ public class ThreadPoolTaskSchedulerBuilder { */ public ThreadPoolTaskSchedulerBuilder poolSize(int poolSize) { return new ThreadPoolTaskSchedulerBuilder(poolSize, this.awaitTermination, this.awaitTerminationPeriod, - this.threadNamePrefix, this.customizers, this.taskDecorator); + this.threadNamePrefix, this.taskDecorator, this.customizers); } /** @@ -113,7 +107,7 @@ public class ThreadPoolTaskSchedulerBuilder { */ public ThreadPoolTaskSchedulerBuilder awaitTermination(boolean awaitTermination) { return new ThreadPoolTaskSchedulerBuilder(this.poolSize, awaitTermination, this.awaitTerminationPeriod, - this.threadNamePrefix, this.customizers, this.taskDecorator); + this.threadNamePrefix, this.taskDecorator, this.customizers); } /** @@ -127,7 +121,7 @@ public class ThreadPoolTaskSchedulerBuilder { */ public ThreadPoolTaskSchedulerBuilder awaitTerminationPeriod(Duration awaitTerminationPeriod) { return new ThreadPoolTaskSchedulerBuilder(this.poolSize, this.awaitTermination, awaitTerminationPeriod, - this.threadNamePrefix, this.customizers, this.taskDecorator); + this.threadNamePrefix, this.taskDecorator, this.customizers); } /** @@ -137,7 +131,7 @@ public class ThreadPoolTaskSchedulerBuilder { */ public ThreadPoolTaskSchedulerBuilder threadNamePrefix(String threadNamePrefix) { return new ThreadPoolTaskSchedulerBuilder(this.poolSize, this.awaitTermination, this.awaitTerminationPeriod, - threadNamePrefix, this.customizers, this.taskDecorator); + threadNamePrefix, this.taskDecorator, this.customizers); } /** @@ -148,7 +142,7 @@ public class ThreadPoolTaskSchedulerBuilder { */ public ThreadPoolTaskSchedulerBuilder taskDecorator(TaskDecorator taskDecorator) { return new ThreadPoolTaskSchedulerBuilder(this.poolSize, this.awaitTermination, this.awaitTerminationPeriod, - this.threadNamePrefix, this.customizers, taskDecorator); + this.threadNamePrefix, taskDecorator, this.customizers); } /** @@ -180,7 +174,7 @@ public class ThreadPoolTaskSchedulerBuilder { Iterable customizers) { Assert.notNull(customizers, "Customizers must not be null"); return new ThreadPoolTaskSchedulerBuilder(this.poolSize, this.awaitTermination, this.awaitTerminationPeriod, - this.threadNamePrefix, append(null, customizers), this.taskDecorator); + this.threadNamePrefix, this.taskDecorator, append(null, customizers)); } /** @@ -210,7 +204,7 @@ public class ThreadPoolTaskSchedulerBuilder { Iterable customizers) { Assert.notNull(customizers, "Customizers must not be null"); return new ThreadPoolTaskSchedulerBuilder(this.poolSize, this.awaitTermination, this.awaitTerminationPeriod, - this.threadNamePrefix, append(this.customizers, customizers), this.taskDecorator); + this.threadNamePrefix, this.taskDecorator, append(this.customizers, customizers)); } /** diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/task/SimpleAsyncTaskSchedulerBuilderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/task/SimpleAsyncTaskSchedulerBuilderTests.java index f2ac75112a..bd951b2ec9 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/task/SimpleAsyncTaskSchedulerBuilderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/task/SimpleAsyncTaskSchedulerBuilderTests.java @@ -62,6 +62,19 @@ class SimpleAsyncTaskSchedulerBuilderTests { assertThat(scheduler).extracting("virtualThreadDelegate").isNotNull(); } + @Test + void taskTerminationTimeoutShouldApply() { + SimpleAsyncTaskScheduler scheduler = this.builder.taskTerminationTimeout(Duration.ofSeconds(1)).build(); + assertThat(scheduler).extracting("taskTerminationTimeout").isEqualTo(1000L); + } + + @Test + void taskDecoratorShouldApply() { + TaskDecorator taskDecorator = mock(TaskDecorator.class); + SimpleAsyncTaskScheduler scheduler = this.builder.taskDecorator(taskDecorator).build(); + assertThat(scheduler).extracting("taskDecorator").isSameAs(taskDecorator); + } + @Test void customizersWhenCustomizersAreNullShouldThrowException() { assertThatIllegalArgumentException() @@ -129,17 +142,4 @@ class SimpleAsyncTaskSchedulerBuilderTests { then(customizer2).should().customize(scheduler); } - @Test - void taskTerminationTimeoutShouldApply() { - SimpleAsyncTaskScheduler scheduler = this.builder.taskTerminationTimeout(Duration.ofSeconds(1)).build(); - assertThat(scheduler).extracting("taskTerminationTimeout").isEqualTo(1000L); - } - - @Test - void taskDecoratorShouldApply() { - TaskDecorator taskDecorator = mock(TaskDecorator.class); - SimpleAsyncTaskScheduler scheduler = this.builder.taskDecorator(taskDecorator).build(); - assertThat(scheduler).extracting("taskDecorator").isSameAs(taskDecorator); - } - } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/task/ThreadPoolTaskSchedulerBuilderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/task/ThreadPoolTaskSchedulerBuilderTests.java index 9411700bfb..83b65ab9fa 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/task/ThreadPoolTaskSchedulerBuilderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/task/ThreadPoolTaskSchedulerBuilderTests.java @@ -65,6 +65,13 @@ class ThreadPoolTaskSchedulerBuilderTests { assertThat(scheduler.getThreadNamePrefix()).isEqualTo("test-"); } + @Test + void taskDecoratorShouldApply() { + TaskDecorator taskDecorator = mock(TaskDecorator.class); + ThreadPoolTaskScheduler scheduler = this.builder.taskDecorator(taskDecorator).build(); + assertThat(scheduler).extracting("taskDecorator").isSameAs(taskDecorator); + } + @Test void customizersWhenCustomizersAreNullShouldThrowException() { assertThatIllegalArgumentException() @@ -132,11 +139,4 @@ class ThreadPoolTaskSchedulerBuilderTests { then(customizer2).should().customize(scheduler); } - @Test - void taskDecoratorShouldApply() { - TaskDecorator taskDecorator = mock(TaskDecorator.class); - ThreadPoolTaskScheduler scheduler = this.builder.taskDecorator(taskDecorator).build(); - assertThat(scheduler).extracting("taskDecorator").isSameAs(taskDecorator); - } - }