diff --git a/framework-docs/modules/ROOT/pages/integration/scheduling.adoc b/framework-docs/modules/ROOT/pages/integration/scheduling.adoc
index e78997fa4ad0..557239daddaa 100644
--- a/framework-docs/modules/ROOT/pages/integration/scheduling.adoc
+++ b/framework-docs/modules/ROOT/pages/integration/scheduling.adoc
@@ -50,6 +50,9 @@ The variants that Spring provides are as follows:
for each invocation. However, it does support a concurrency limit that blocks
any invocations that are over the limit until a slot has been freed up. If you
are looking for true pooling, see `ThreadPoolTaskExecutor`, later in this list.
+ This will use JDK 21's Virtual Threads, when the "virtualThreads"
+ option is enabled. This implementation also supports graceful shutdown through
+ Spring's lifecycle management.
* `ConcurrentTaskExecutor`:
This implementation is an adapter for a `java.util.concurrent.Executor` instance.
There is an alternative (`ThreadPoolTaskExecutor`) that exposes the `Executor`
@@ -61,15 +64,13 @@ The variants that Spring provides are as follows:
a `java.util.concurrent.ThreadPoolExecutor` and wraps it in a `TaskExecutor`.
If you need to adapt to a different kind of `java.util.concurrent.Executor`,
we recommend that you use a `ConcurrentTaskExecutor` instead.
+ It also provides a pause/resume capability and graceful shutdown through
+ Spring's lifecycle management.
* `DefaultManagedTaskExecutor`:
This implementation uses a JNDI-obtained `ManagedExecutorService` in a JSR-236
compatible runtime environment (such as a Jakarta EE application server),
replacing a CommonJ WorkManager for that purpose.
-As of 6.1, `ThreadPoolTaskExecutor` provides a pause/resume capability and graceful
-shutdown through Spring's lifecycle management. There is also a new "virtualThreads"
-option on `SimpleAsyncTaskExecutor` which is aligned with JDK 21's Virtual Threads,
-as well as a graceful shutdown capability for `SimpleAsyncTaskExecutor` as well.
[[scheduling-task-executor-usage]]
@@ -89,6 +90,22 @@ To configure the rules that the `TaskExecutor` uses, we expose simple bean prope
include-code::./TaskExecutorConfiguration[tag=snippet,indent=0]
+Most `TaskExecutor` implementations provide a way to automatically wrap tasks submitted
+with a `TaskDecorator`. Decorators should delegate to the task it is wrapping, possibly
+implementing custom behavior before/after the execution of the task.
+
+Let's consider a simple implementation that will log messages before and after the execution
+or our tasks:
+
+include-code::./LoggingTaskDecorator[indent=0]
+
+We can then configure our decorator on a `TaskExecutor` instance:
+
+include-code::./TaskExecutorConfiguration[tag=decorator,indent=0]
+
+In case multiple decorators are needed, the `org.springframework.core.task.support.CompositeTaskDecorator`
+can be used to execute sequentially multiple decorators.
+
[[scheduling-task-scheduler]]
== The Spring `TaskScheduler` Abstraction
diff --git a/framework-docs/src/main/java/org/springframework/docs/integration/schedulingtaskexecutorusage/LoggingTaskDecorator.java b/framework-docs/src/main/java/org/springframework/docs/integration/schedulingtaskexecutorusage/LoggingTaskDecorator.java
new file mode 100644
index 000000000000..2f92fff0e4c5
--- /dev/null
+++ b/framework-docs/src/main/java/org/springframework/docs/integration/schedulingtaskexecutorusage/LoggingTaskDecorator.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2002-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.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.docs.integration.schedulingtaskexecutorusage;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.core.task.TaskDecorator;
+
+public class LoggingTaskDecorator implements TaskDecorator {
+
+ private static final Log logger = LogFactory.getLog(LoggingTaskDecorator.class);
+
+ @Override
+ public Runnable decorate(Runnable runnable) {
+ return () -> {
+ logger.debug("Before execution of " + runnable);
+ runnable.run();
+ logger.debug("After execution of " + runnable);
+ };
+ }
+}
diff --git a/framework-docs/src/main/java/org/springframework/docs/integration/schedulingtaskexecutorusage/TaskExecutorConfiguration.java b/framework-docs/src/main/java/org/springframework/docs/integration/schedulingtaskexecutorusage/TaskExecutorConfiguration.java
index c3beb0be046c..d5dd6ceab451 100644
--- a/framework-docs/src/main/java/org/springframework/docs/integration/schedulingtaskexecutorusage/TaskExecutorConfiguration.java
+++ b/framework-docs/src/main/java/org/springframework/docs/integration/schedulingtaskexecutorusage/TaskExecutorConfiguration.java
@@ -38,4 +38,13 @@ TaskExecutorExample taskExecutorExample(ThreadPoolTaskExecutor taskExecutor) {
return new TaskExecutorExample(taskExecutor);
}
// end::snippet[]
+
+ // tag::decorator[]
+ @Bean
+ ThreadPoolTaskExecutor decoratedTaskExecutor() {
+ ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
+ taskExecutor.setTaskDecorator(new LoggingTaskDecorator());
+ return taskExecutor;
+ }
+ // end::decorator[]
}
diff --git a/framework-docs/src/main/kotlin/org/springframework/docs/integration/schedulingtaskexecutorusage/LoggingTaskDecorator.kt b/framework-docs/src/main/kotlin/org/springframework/docs/integration/schedulingtaskexecutorusage/LoggingTaskDecorator.kt
new file mode 100644
index 000000000000..a9501bf7b83d
--- /dev/null
+++ b/framework-docs/src/main/kotlin/org/springframework/docs/integration/schedulingtaskexecutorusage/LoggingTaskDecorator.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2002-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.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.docs.integration.schedulingtaskexecutorusage
+
+import org.apache.commons.logging.Log
+import org.apache.commons.logging.LogFactory
+import org.springframework.core.task.TaskDecorator
+
+class LoggingTaskDecorator : TaskDecorator {
+
+ override fun decorate(runnable: Runnable): Runnable {
+ return Runnable {
+ logger.debug("Before execution of $runnable")
+ runnable.run()
+ logger.debug("After execution of $runnable")
+ }
+ }
+
+ companion object {
+ private val logger: Log = LogFactory.getLog(
+ LoggingTaskDecorator::class.java
+ )
+ }
+}
diff --git a/framework-docs/src/main/kotlin/org/springframework/docs/integration/schedulingtaskexecutorusage/TaskExecutorConfiguration.kt b/framework-docs/src/main/kotlin/org/springframework/docs/integration/schedulingtaskexecutorusage/TaskExecutorConfiguration.kt
index 1a2dd1a8861d..dea4510ac937 100644
--- a/framework-docs/src/main/kotlin/org/springframework/docs/integration/schedulingtaskexecutorusage/TaskExecutorConfiguration.kt
+++ b/framework-docs/src/main/kotlin/org/springframework/docs/integration/schedulingtaskexecutorusage/TaskExecutorConfiguration.kt
@@ -34,4 +34,11 @@ class TaskExecutorConfiguration {
@Bean
fun taskExecutorExample(taskExecutor: ThreadPoolTaskExecutor) = TaskExecutorExample(taskExecutor)
// end::snippet[]
+
+ // tag::decorator[]
+ @Bean
+ fun decoratedTaskExecutor() = ThreadPoolTaskExecutor().apply {
+ setTaskDecorator(LoggingTaskDecorator())
+ }
+ // end::decorator[]
}
diff --git a/framework-docs/src/main/resources/org/springframework/docs/integration/schedulingtaskexecutorusage/TaskExecutorConfiguration.xml b/framework-docs/src/main/resources/org/springframework/docs/integration/schedulingtaskexecutorusage/TaskExecutorConfiguration.xml
index a0331c308966..96fc1be4e904 100644
--- a/framework-docs/src/main/resources/org/springframework/docs/integration/schedulingtaskexecutorusage/TaskExecutorConfiguration.xml
+++ b/framework-docs/src/main/resources/org/springframework/docs/integration/schedulingtaskexecutorusage/TaskExecutorConfiguration.xml
@@ -15,4 +15,13 @@
+
+
+
+
+
+
+
+
+