2022-11-26 00:33:07 +08:00
|
|
|
[[scheduling]]
|
|
|
|
= Task Execution and Scheduling
|
|
|
|
|
|
|
|
The Spring Framework provides abstractions for the asynchronous execution and scheduling of
|
|
|
|
tasks with the `TaskExecutor` and `TaskScheduler` interfaces, respectively. Spring also
|
|
|
|
features implementations of those interfaces that support thread pools or delegation to
|
|
|
|
CommonJ within an application server environment. Ultimately, the use of these
|
2023-09-11 23:36:57 +08:00
|
|
|
implementations behind the common interfaces abstracts away the differences between
|
|
|
|
Java SE and Jakarta EE environments.
|
2022-11-26 00:33:07 +08:00
|
|
|
|
2023-09-11 23:36:57 +08:00
|
|
|
Spring also features integration classes to support scheduling with the
|
|
|
|
https://www.quartz-scheduler.org/[Quartz Scheduler].
|
2022-11-26 00:33:07 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[scheduling-task-executor]]
|
|
|
|
== The Spring `TaskExecutor` Abstraction
|
|
|
|
|
|
|
|
Executors are the JDK name for the concept of thread pools. The "`executor`" naming is
|
|
|
|
due to the fact that there is no guarantee that the underlying implementation is
|
|
|
|
actually a pool. An executor may be single-threaded or even synchronous. Spring's
|
|
|
|
abstraction hides implementation details between the Java SE and Jakarta EE environments.
|
|
|
|
|
|
|
|
Spring's `TaskExecutor` interface is identical to the `java.util.concurrent.Executor`
|
|
|
|
interface. In fact, originally, its primary reason for existence was to abstract away
|
|
|
|
the need for Java 5 when using thread pools. The interface has a single method
|
|
|
|
(`execute(Runnable task)`) that accepts a task for execution based on the semantics
|
|
|
|
and configuration of the thread pool.
|
|
|
|
|
|
|
|
The `TaskExecutor` was originally created to give other Spring components an abstraction
|
|
|
|
for thread pooling where needed. Components such as the `ApplicationEventMulticaster`,
|
|
|
|
JMS's `AbstractMessageListenerContainer`, and Quartz integration all use the
|
|
|
|
`TaskExecutor` abstraction to pool threads. However, if your beans need thread pooling
|
|
|
|
behavior, you can also use this abstraction for your own needs.
|
|
|
|
|
|
|
|
|
|
|
|
[[scheduling-task-executor-types]]
|
|
|
|
=== `TaskExecutor` Types
|
|
|
|
|
|
|
|
Spring includes a number of pre-built implementations of `TaskExecutor`.
|
|
|
|
In all likelihood, you should never need to implement your own.
|
|
|
|
The variants that Spring provides are as follows:
|
|
|
|
|
|
|
|
* `SyncTaskExecutor`:
|
|
|
|
This implementation does not run invocations asynchronously. Instead, each
|
|
|
|
invocation takes place in the calling thread. It is primarily used in situations
|
|
|
|
where multi-threading is not necessary, such as in simple test cases.
|
|
|
|
* `SimpleAsyncTaskExecutor`:
|
|
|
|
This implementation does not reuse any threads. Rather, it starts up a new thread
|
|
|
|
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.
|
2024-09-06 03:04:22 +08:00
|
|
|
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.
|
2022-11-26 00:33:07 +08:00
|
|
|
* `ConcurrentTaskExecutor`:
|
|
|
|
This implementation is an adapter for a `java.util.concurrent.Executor` instance.
|
|
|
|
There is an alternative (`ThreadPoolTaskExecutor`) that exposes the `Executor`
|
|
|
|
configuration parameters as bean properties. There is rarely a need to use
|
|
|
|
`ConcurrentTaskExecutor` directly. However, if the `ThreadPoolTaskExecutor` is not
|
|
|
|
flexible enough for your needs, `ConcurrentTaskExecutor` is an alternative.
|
|
|
|
* `ThreadPoolTaskExecutor`:
|
2023-07-28 03:39:58 +08:00
|
|
|
This implementation is most commonly used. It exposes bean properties for configuring
|
|
|
|
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.
|
2024-09-06 03:04:22 +08:00
|
|
|
It also provides a pause/resume capability and graceful shutdown through
|
|
|
|
Spring's lifecycle management.
|
2022-11-26 00:33:07 +08:00
|
|
|
* `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.
|
|
|
|
|
2023-07-28 03:39:58 +08:00
|
|
|
|
2022-11-26 00:33:07 +08:00
|
|
|
|
|
|
|
[[scheduling-task-executor-usage]]
|
|
|
|
=== Using a `TaskExecutor`
|
|
|
|
|
2023-07-28 03:39:58 +08:00
|
|
|
Spring's `TaskExecutor` implementations are commonly used with dependency injection.
|
|
|
|
In the following example, we define a bean that uses the `ThreadPoolTaskExecutor`
|
|
|
|
to asynchronously print out a set of messages:
|
2022-11-26 00:33:07 +08:00
|
|
|
|
2024-05-06 22:05:15 +08:00
|
|
|
include-code::./TaskExecutorExample[tag=snippet,indent=0]
|
2022-11-26 00:33:07 +08:00
|
|
|
|
|
|
|
As you can see, rather than retrieving a thread from the pool and executing it yourself,
|
|
|
|
you add your `Runnable` to the queue. Then the `TaskExecutor` uses its internal rules to
|
|
|
|
decide when the task gets run.
|
|
|
|
|
|
|
|
To configure the rules that the `TaskExecutor` uses, we expose simple bean properties:
|
|
|
|
|
2024-05-06 22:05:15 +08:00
|
|
|
include-code::./TaskExecutorConfiguration[tag=snippet,indent=0]
|
2022-11-26 00:33:07 +08:00
|
|
|
|
2024-09-06 03:04:22 +08:00
|
|
|
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.
|
|
|
|
|
2022-11-26 00:33:07 +08:00
|
|
|
|
|
|
|
[[scheduling-task-scheduler]]
|
|
|
|
== The Spring `TaskScheduler` Abstraction
|
|
|
|
|
2023-01-20 18:44:34 +08:00
|
|
|
In addition to the `TaskExecutor` abstraction, Spring has a `TaskScheduler` SPI with a
|
|
|
|
variety of methods for scheduling tasks to run at some point in the future. The following
|
|
|
|
listing shows the `TaskScheduler` interface definition:
|
2022-11-26 00:33:07 +08:00
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
public interface TaskScheduler {
|
|
|
|
|
2023-01-20 18:44:34 +08:00
|
|
|
Clock getClock();
|
|
|
|
|
2022-11-26 00:33:07 +08:00
|
|
|
ScheduledFuture schedule(Runnable task, Trigger trigger);
|
|
|
|
|
|
|
|
ScheduledFuture schedule(Runnable task, Instant startTime);
|
|
|
|
|
|
|
|
ScheduledFuture scheduleAtFixedRate(Runnable task, Instant startTime, Duration period);
|
|
|
|
|
|
|
|
ScheduledFuture scheduleAtFixedRate(Runnable task, Duration period);
|
|
|
|
|
|
|
|
ScheduledFuture scheduleWithFixedDelay(Runnable task, Instant startTime, Duration delay);
|
|
|
|
|
|
|
|
ScheduledFuture scheduleWithFixedDelay(Runnable task, Duration delay);
|
|
|
|
|
|
|
|
----
|
|
|
|
|
|
|
|
The simplest method is the one named `schedule` that takes only a `Runnable` and an `Instant`.
|
|
|
|
That causes the task to run once after the specified time. All of the other methods
|
|
|
|
are capable of scheduling tasks to run repeatedly. The fixed-rate and fixed-delay
|
|
|
|
methods are for simple, periodic execution, but the method that accepts a `Trigger` is
|
|
|
|
much more flexible.
|
|
|
|
|
|
|
|
|
|
|
|
[[scheduling-trigger-interface]]
|
|
|
|
=== `Trigger` Interface
|
|
|
|
|
2023-01-20 18:44:34 +08:00
|
|
|
The `Trigger` interface is essentially inspired by JSR-236. The basic idea of the
|
|
|
|
`Trigger` is that execution times may be determined based on past execution outcomes or
|
|
|
|
even arbitrary conditions. If these determinations take into account the outcome of the
|
|
|
|
preceding execution, that information is available within a `TriggerContext`. The
|
|
|
|
`Trigger` interface itself is quite simple, as the following listing shows:
|
2022-11-26 00:33:07 +08:00
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
public interface Trigger {
|
|
|
|
|
2022-12-18 20:21:27 +08:00
|
|
|
Instant nextExecution(TriggerContext triggerContext);
|
2022-11-26 00:33:07 +08:00
|
|
|
}
|
|
|
|
----
|
|
|
|
|
|
|
|
The `TriggerContext` is the most important part. It encapsulates all of
|
|
|
|
the relevant data and is open for extension in the future, if necessary. The
|
|
|
|
`TriggerContext` is an interface (a `SimpleTriggerContext` implementation is used by
|
|
|
|
default). The following listing shows the available methods for `Trigger` implementations.
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
public interface TriggerContext {
|
|
|
|
|
2023-01-20 18:44:34 +08:00
|
|
|
Clock getClock();
|
|
|
|
|
2022-12-18 20:21:27 +08:00
|
|
|
Instant lastScheduledExecution();
|
2022-11-26 00:33:07 +08:00
|
|
|
|
2022-12-18 20:21:27 +08:00
|
|
|
Instant lastActualExecution();
|
2022-11-26 00:33:07 +08:00
|
|
|
|
2022-12-18 20:21:27 +08:00
|
|
|
Instant lastCompletion();
|
2022-11-26 00:33:07 +08:00
|
|
|
}
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
|
|
[[scheduling-trigger-implementations]]
|
|
|
|
=== `Trigger` Implementations
|
|
|
|
|
|
|
|
Spring provides two implementations of the `Trigger` interface. The most interesting one
|
|
|
|
is the `CronTrigger`. It enables the scheduling of tasks based on
|
2023-04-19 23:26:17 +08:00
|
|
|
xref:integration/scheduling.adoc#scheduling-cron-expression[cron expressions].
|
2022-11-26 00:33:07 +08:00
|
|
|
For example, the following task is scheduled to run 15 minutes past each hour but only
|
2022-12-18 20:20:46 +08:00
|
|
|
during the 9-to-5 "business hours" on weekdays:
|
2022-11-26 00:33:07 +08:00
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
[subs="verbatim"]
|
|
|
|
----
|
|
|
|
scheduler.schedule(task, new CronTrigger("0 15 9-17 * * MON-FRI"));
|
|
|
|
----
|
|
|
|
|
|
|
|
The other implementation is a `PeriodicTrigger` that accepts a fixed
|
|
|
|
period, an optional initial delay value, and a boolean to indicate whether the period
|
|
|
|
should be interpreted as a fixed-rate or a fixed-delay. Since the `TaskScheduler`
|
|
|
|
interface already defines methods for scheduling tasks at a fixed rate or with a
|
|
|
|
fixed delay, those methods should be used directly whenever possible. The value of the
|
|
|
|
`PeriodicTrigger` implementation is that you can use it within components that rely on
|
|
|
|
the `Trigger` abstraction. For example, it may be convenient to allow periodic triggers,
|
|
|
|
cron-based triggers, and even custom trigger implementations to be used interchangeably.
|
2023-07-28 03:39:58 +08:00
|
|
|
Such a component could take advantage of dependency injection so that you can configure
|
|
|
|
such `Triggers` externally and, therefore, easily modify or extend them.
|
2022-11-26 00:33:07 +08:00
|
|
|
|
|
|
|
|
|
|
|
[[scheduling-task-scheduler-implementations]]
|
|
|
|
=== `TaskScheduler` implementations
|
|
|
|
|
|
|
|
As with Spring's `TaskExecutor` abstraction, the primary benefit of the `TaskScheduler`
|
|
|
|
arrangement is that an application's scheduling needs are decoupled from the deployment
|
|
|
|
environment. This abstraction level is particularly relevant when deploying to an
|
|
|
|
application server environment where threads should not be created directly by the
|
2023-07-28 03:39:58 +08:00
|
|
|
application itself. For such scenarios, Spring provides a `DefaultManagedTaskScheduler`
|
|
|
|
that delegates to a JSR-236 `ManagedScheduledExecutorService` in a Jakarta EE environment.
|
2022-11-26 00:33:07 +08:00
|
|
|
|
|
|
|
Whenever external thread management is not a requirement, a simpler alternative is
|
|
|
|
a local `ScheduledExecutorService` setup within the application, which can be adapted
|
|
|
|
through Spring's `ConcurrentTaskScheduler`. As a convenience, Spring also provides a
|
|
|
|
`ThreadPoolTaskScheduler`, which internally delegates to a `ScheduledExecutorService`
|
|
|
|
to provide common bean-style configuration along the lines of `ThreadPoolTaskExecutor`.
|
|
|
|
These variants work perfectly fine for locally embedded thread pool setups in lenient
|
|
|
|
application server environments, as well -- in particular on Tomcat and Jetty.
|
|
|
|
|
2023-07-28 03:39:58 +08:00
|
|
|
As of 6.1, `ThreadPoolTaskScheduler` provides a pause/resume capability and graceful
|
|
|
|
shutdown through Spring's lifecycle management. There is also a new option called
|
|
|
|
`SimpleAsyncTaskScheduler` which is aligned with JDK 21's Virtual Threads, using a
|
2023-12-26 17:59:13 +08:00
|
|
|
single scheduler thread but firing up a new thread for every scheduled task execution
|
|
|
|
(except for fixed-delay tasks which all operate on a single scheduler thread, so for
|
|
|
|
this virtual-thread-aligned option, fixed rates and cron triggers are recommended).
|
2023-07-28 03:39:58 +08:00
|
|
|
|
2022-11-26 00:33:07 +08:00
|
|
|
|
|
|
|
|
|
|
|
[[scheduling-annotation-support]]
|
|
|
|
== Annotation Support for Scheduling and Asynchronous Execution
|
|
|
|
|
|
|
|
Spring provides annotation support for both task scheduling and asynchronous method
|
|
|
|
execution.
|
|
|
|
|
|
|
|
|
|
|
|
[[scheduling-enable-annotation-support]]
|
|
|
|
=== Enable Scheduling Annotations
|
|
|
|
|
2023-09-11 23:36:57 +08:00
|
|
|
To enable support for `@Scheduled` and `@Async` annotations, you can add `@EnableScheduling`
|
2024-05-06 22:05:15 +08:00
|
|
|
and `@EnableAsync` to one of your `@Configuration` classes, or `<task:annotation-driven>` element,
|
|
|
|
as the following example shows:
|
2022-11-26 00:33:07 +08:00
|
|
|
|
2024-05-06 22:05:15 +08:00
|
|
|
include-code::./SchedulingConfiguration[tag=snippet,indent=0]
|
2022-11-26 00:33:07 +08:00
|
|
|
|
|
|
|
You can pick and choose the relevant annotations for your application. For example,
|
|
|
|
if you need only support for `@Scheduled`, you can omit `@EnableAsync`. For more
|
|
|
|
fine-grained control, you can additionally implement the `SchedulingConfigurer`
|
|
|
|
interface, the `AsyncConfigurer` interface, or both. See the
|
2023-11-21 22:59:24 +08:00
|
|
|
{spring-framework-api}/scheduling/annotation/SchedulingConfigurer.html[`SchedulingConfigurer`]
|
|
|
|
and {spring-framework-api}/scheduling/annotation/AsyncConfigurer.html[`AsyncConfigurer`]
|
2022-11-26 00:33:07 +08:00
|
|
|
javadoc for full details.
|
|
|
|
|
|
|
|
Note that, with the preceding XML, an executor reference is provided for handling those
|
|
|
|
tasks that correspond to methods with the `@Async` annotation, and the scheduler
|
|
|
|
reference is provided for managing those methods annotated with `@Scheduled`.
|
|
|
|
|
|
|
|
NOTE: The default advice mode for processing `@Async` annotations is `proxy` which allows
|
|
|
|
for interception of calls through the proxy only. Local calls within the same class
|
|
|
|
cannot get intercepted that way. For a more advanced mode of interception, consider
|
|
|
|
switching to `aspectj` mode in combination with compile-time or load-time weaving.
|
|
|
|
|
|
|
|
|
|
|
|
[[scheduling-annotation-support-scheduled]]
|
|
|
|
=== The `@Scheduled` annotation
|
|
|
|
|
|
|
|
You can add the `@Scheduled` annotation to a method, along with trigger metadata. For
|
|
|
|
example, the following method is invoked every five seconds (5000 milliseconds) with a
|
|
|
|
fixed delay, meaning that the period is measured from the completion time of each
|
|
|
|
preceding invocation.
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
@Scheduled(fixedDelay = 5000)
|
|
|
|
public void doSomething() {
|
|
|
|
// something that should run periodically
|
|
|
|
}
|
|
|
|
----
|
|
|
|
|
|
|
|
[NOTE]
|
|
|
|
====
|
|
|
|
By default, milliseconds will be used as the time unit for fixed delay, fixed rate, and
|
|
|
|
initial delay values. If you would like to use a different time unit such as seconds or
|
|
|
|
minutes, you can configure this via the `timeUnit` attribute in `@Scheduled`.
|
|
|
|
|
|
|
|
For example, the previous example can also be written as follows.
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
@Scheduled(fixedDelay = 5, timeUnit = TimeUnit.SECONDS)
|
|
|
|
public void doSomething() {
|
|
|
|
// something that should run periodically
|
|
|
|
}
|
|
|
|
----
|
|
|
|
====
|
|
|
|
|
|
|
|
If you need a fixed-rate execution, you can use the `fixedRate` attribute within the
|
|
|
|
annotation. The following method is invoked every five seconds (measured between the
|
2023-09-13 22:48:54 +08:00
|
|
|
successive start times of each invocation):
|
2022-11-26 00:33:07 +08:00
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
@Scheduled(fixedRate = 5, timeUnit = TimeUnit.SECONDS)
|
|
|
|
public void doSomething() {
|
|
|
|
// something that should run periodically
|
|
|
|
}
|
|
|
|
----
|
|
|
|
|
2023-09-13 22:48:54 +08:00
|
|
|
For fixed-delay and fixed-rate tasks, you can specify an initial delay by indicating
|
|
|
|
the amount of time to wait before the first execution of the method, as the following
|
|
|
|
`fixedRate` example shows:
|
2022-11-26 00:33:07 +08:00
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
@Scheduled(initialDelay = 1000, fixedRate = 5000)
|
|
|
|
public void doSomething() {
|
|
|
|
// something that should run periodically
|
|
|
|
}
|
|
|
|
----
|
|
|
|
|
2023-09-13 22:48:54 +08:00
|
|
|
For one-time tasks, you can just specify an initial delay by indicating the amount
|
|
|
|
of time to wait before the intended execution of the method:
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
@Scheduled(initialDelay = 1000)
|
|
|
|
public void doSomething() {
|
|
|
|
// something that should run only once
|
|
|
|
}
|
|
|
|
----
|
|
|
|
|
2022-11-26 00:33:07 +08:00
|
|
|
If simple periodic scheduling is not expressive enough, you can provide a
|
2023-04-19 23:26:17 +08:00
|
|
|
xref:integration/scheduling.adoc#scheduling-cron-expression[cron expression].
|
2022-11-26 00:33:07 +08:00
|
|
|
The following example runs only on weekdays:
|
|
|
|
|
|
|
|
[source,java,indent=0]
|
|
|
|
[subs="verbatim"]
|
|
|
|
----
|
|
|
|
@Scheduled(cron="*/5 * * * * MON-FRI")
|
|
|
|
public void doSomething() {
|
|
|
|
// something that should run on weekdays only
|
|
|
|
}
|
|
|
|
----
|
|
|
|
|
|
|
|
TIP: You can also use the `zone` attribute to specify the time zone in which the cron
|
|
|
|
expression is resolved.
|
|
|
|
|
|
|
|
Notice that the methods to be scheduled must have void returns and must not accept any
|
|
|
|
arguments. If the method needs to interact with other objects from the application
|
|
|
|
context, those would typically have been provided through dependency injection.
|
|
|
|
|
2023-07-14 20:37:28 +08:00
|
|
|
`@Scheduled` can be used as a repeatable annotation. If several scheduled declarations
|
|
|
|
are found on the same method, each of them will be processed independently, with a
|
|
|
|
separate trigger firing for each of them. As a consequence, such co-located schedules
|
|
|
|
may overlap and execute multiple times in parallel or in immediate succession.
|
|
|
|
Please make sure that your specified cron expressions etc do not accidentally overlap.
|
|
|
|
|
2022-11-26 00:33:07 +08:00
|
|
|
[NOTE]
|
|
|
|
====
|
|
|
|
As of Spring Framework 4.3, `@Scheduled` methods are supported on beans of any scope.
|
|
|
|
|
|
|
|
Make sure that you are not initializing multiple instances of the same `@Scheduled`
|
|
|
|
annotation class at runtime, unless you do want to schedule callbacks to each such
|
|
|
|
instance. Related to this, make sure that you do not use `@Configurable` on bean
|
|
|
|
classes that are annotated with `@Scheduled` and registered as regular Spring beans
|
|
|
|
with the container. Otherwise, you would get double initialization (once through the
|
|
|
|
container and once through the `@Configurable` aspect), with the consequence of each
|
|
|
|
`@Scheduled` method being invoked twice.
|
|
|
|
====
|
|
|
|
|
Support `@Scheduled` fixedDelay/fixedRate on Publisher-returning methods
This commit adds support for `@Scheduled` annotation on reactive
methods and Kotlin suspending functions.
Reactive methods are methods that return a `Publisher` or a subclass
of `Publisher`. The `ReactiveAdapterRegistry` is used to support many
implementations, such as `Flux`, `Mono`, `Flow`, `Single`, etc.
Methods should not take any argument and published values will be
ignored, as they are already with synchronous support.
This is implemented in `ScheduledAnnotationReactiveSupport`, which
"converts" Publishers to `Runnable`. This strategy keeps track of
active Subscriptions in the `ScheduledAnnotationBeanPostProcessor`,
in order to cancel them all in case of shutdown.
The existing scheduling support for tasks is reused, aligning the
triggering behavior with the existing support: cron, fixedDelay and
fixedRate are all supported strategies.
If the `Publisher` errors, the exception is logged at warn level and
otherwise ignored. As a result new `Runnable` instances will be
created for each execution and scheduling will continue.
The only difference with synchronous support is that error signals
will not be thrown by those `Runnable` tasks and will not be made
available to the `org.springframework.util.ErrorHandler` contract.
This is due to the asynchronous and lazy nature of Publishers.
Closes gh-23533
Closes gh-28515
2023-06-05 18:25:15 +08:00
|
|
|
[[scheduling-annotation-support-scheduled-reactive]]
|
|
|
|
=== The `@Scheduled` annotation on Reactive methods or Kotlin suspending functions
|
|
|
|
|
|
|
|
As of Spring Framework 6.1, `@Scheduled` methods are also supported on several types
|
|
|
|
of reactive methods:
|
|
|
|
|
|
|
|
- methods with a `Publisher` return type (or any concrete implementation of `Publisher`)
|
|
|
|
like in the following example:
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
@Scheduled(fixedDelay = 500)
|
|
|
|
public Publisher<Void> reactiveSomething() {
|
|
|
|
// return an instance of Publisher
|
|
|
|
}
|
|
|
|
----
|
|
|
|
|
|
|
|
- methods with a return type that can be adapted to `Publisher` via the shared instance
|
|
|
|
of the `ReactiveAdapterRegistry`, provided the type supports _deferred subscription_ like
|
|
|
|
in the following example:
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
@Scheduled(fixedDelay = 500)
|
|
|
|
public Single<String> rxjavaNonPublisher() {
|
|
|
|
return Single.just("example");
|
|
|
|
}
|
|
|
|
----
|
|
|
|
|
|
|
|
[NOTE]
|
|
|
|
====
|
|
|
|
The `CompletableFuture` class is an example of a type that can typically be adapted
|
|
|
|
to `Publisher` but doesn't support deferred subscription. Its `ReactiveAdapter` in the
|
|
|
|
registry denotes that by having the `getDescriptor().isDeferred()` method return `false`.
|
|
|
|
====
|
|
|
|
|
|
|
|
- Kotlin suspending functions, like in the following example:
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
@Scheduled(fixedDelay = 500)
|
|
|
|
suspend fun something() {
|
|
|
|
// do something asynchronous
|
|
|
|
}
|
|
|
|
----
|
|
|
|
|
|
|
|
- methods that return a Kotlin `Flow` or `Deferred` instance, like in the following example:
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
@Scheduled(fixedDelay = 500)
|
|
|
|
fun something(): Flow<Void> {
|
|
|
|
flow {
|
|
|
|
// do something asynchronous
|
|
|
|
}
|
|
|
|
}
|
|
|
|
----
|
|
|
|
|
|
|
|
All these types of methods must be declared without any arguments. In the case of Kotlin
|
2023-06-06 17:29:25 +08:00
|
|
|
suspending functions, the `kotlinx.coroutines.reactor` bridge must also be present to allow
|
Support `@Scheduled` fixedDelay/fixedRate on Publisher-returning methods
This commit adds support for `@Scheduled` annotation on reactive
methods and Kotlin suspending functions.
Reactive methods are methods that return a `Publisher` or a subclass
of `Publisher`. The `ReactiveAdapterRegistry` is used to support many
implementations, such as `Flux`, `Mono`, `Flow`, `Single`, etc.
Methods should not take any argument and published values will be
ignored, as they are already with synchronous support.
This is implemented in `ScheduledAnnotationReactiveSupport`, which
"converts" Publishers to `Runnable`. This strategy keeps track of
active Subscriptions in the `ScheduledAnnotationBeanPostProcessor`,
in order to cancel them all in case of shutdown.
The existing scheduling support for tasks is reused, aligning the
triggering behavior with the existing support: cron, fixedDelay and
fixedRate are all supported strategies.
If the `Publisher` errors, the exception is logged at warn level and
otherwise ignored. As a result new `Runnable` instances will be
created for each execution and scheduling will continue.
The only difference with synchronous support is that error signals
will not be thrown by those `Runnable` tasks and will not be made
available to the `org.springframework.util.ErrorHandler` contract.
This is due to the asynchronous and lazy nature of Publishers.
Closes gh-23533
Closes gh-28515
2023-06-05 18:25:15 +08:00
|
|
|
the framework to invoke a suspending function as a `Publisher`.
|
|
|
|
|
2023-06-06 17:29:25 +08:00
|
|
|
The Spring Framework will obtain a `Publisher` for the annotated method once and will
|
Support `@Scheduled` fixedDelay/fixedRate on Publisher-returning methods
This commit adds support for `@Scheduled` annotation on reactive
methods and Kotlin suspending functions.
Reactive methods are methods that return a `Publisher` or a subclass
of `Publisher`. The `ReactiveAdapterRegistry` is used to support many
implementations, such as `Flux`, `Mono`, `Flow`, `Single`, etc.
Methods should not take any argument and published values will be
ignored, as they are already with synchronous support.
This is implemented in `ScheduledAnnotationReactiveSupport`, which
"converts" Publishers to `Runnable`. This strategy keeps track of
active Subscriptions in the `ScheduledAnnotationBeanPostProcessor`,
in order to cancel them all in case of shutdown.
The existing scheduling support for tasks is reused, aligning the
triggering behavior with the existing support: cron, fixedDelay and
fixedRate are all supported strategies.
If the `Publisher` errors, the exception is logged at warn level and
otherwise ignored. As a result new `Runnable` instances will be
created for each execution and scheduling will continue.
The only difference with synchronous support is that error signals
will not be thrown by those `Runnable` tasks and will not be made
available to the `org.springframework.util.ErrorHandler` contract.
This is due to the asynchronous and lazy nature of Publishers.
Closes gh-23533
Closes gh-28515
2023-06-05 18:25:15 +08:00
|
|
|
schedule a `Runnable` in which it subscribes to said `Publisher`. These inner regular
|
2023-12-24 05:06:08 +08:00
|
|
|
subscriptions occur according to the corresponding `cron`/`fixedDelay`/`fixedRate` configuration.
|
Support `@Scheduled` fixedDelay/fixedRate on Publisher-returning methods
This commit adds support for `@Scheduled` annotation on reactive
methods and Kotlin suspending functions.
Reactive methods are methods that return a `Publisher` or a subclass
of `Publisher`. The `ReactiveAdapterRegistry` is used to support many
implementations, such as `Flux`, `Mono`, `Flow`, `Single`, etc.
Methods should not take any argument and published values will be
ignored, as they are already with synchronous support.
This is implemented in `ScheduledAnnotationReactiveSupport`, which
"converts" Publishers to `Runnable`. This strategy keeps track of
active Subscriptions in the `ScheduledAnnotationBeanPostProcessor`,
in order to cancel them all in case of shutdown.
The existing scheduling support for tasks is reused, aligning the
triggering behavior with the existing support: cron, fixedDelay and
fixedRate are all supported strategies.
If the `Publisher` errors, the exception is logged at warn level and
otherwise ignored. As a result new `Runnable` instances will be
created for each execution and scheduling will continue.
The only difference with synchronous support is that error signals
will not be thrown by those `Runnable` tasks and will not be made
available to the `org.springframework.util.ErrorHandler` contract.
This is due to the asynchronous and lazy nature of Publishers.
Closes gh-23533
Closes gh-28515
2023-06-05 18:25:15 +08:00
|
|
|
|
|
|
|
If the `Publisher` emits `onNext` signal(s), these are ignored and discarded (the same way
|
|
|
|
return values from synchronous `@Scheduled` methods are ignored).
|
|
|
|
|
2023-12-24 05:06:08 +08:00
|
|
|
In the following example, the `Flux` emits `onNext("Hello")`, `onNext("World")` every 5
|
Support `@Scheduled` fixedDelay/fixedRate on Publisher-returning methods
This commit adds support for `@Scheduled` annotation on reactive
methods and Kotlin suspending functions.
Reactive methods are methods that return a `Publisher` or a subclass
of `Publisher`. The `ReactiveAdapterRegistry` is used to support many
implementations, such as `Flux`, `Mono`, `Flow`, `Single`, etc.
Methods should not take any argument and published values will be
ignored, as they are already with synchronous support.
This is implemented in `ScheduledAnnotationReactiveSupport`, which
"converts" Publishers to `Runnable`. This strategy keeps track of
active Subscriptions in the `ScheduledAnnotationBeanPostProcessor`,
in order to cancel them all in case of shutdown.
The existing scheduling support for tasks is reused, aligning the
triggering behavior with the existing support: cron, fixedDelay and
fixedRate are all supported strategies.
If the `Publisher` errors, the exception is logged at warn level and
otherwise ignored. As a result new `Runnable` instances will be
created for each execution and scheduling will continue.
The only difference with synchronous support is that error signals
will not be thrown by those `Runnable` tasks and will not be made
available to the `org.springframework.util.ErrorHandler` contract.
This is due to the asynchronous and lazy nature of Publishers.
Closes gh-23533
Closes gh-28515
2023-06-05 18:25:15 +08:00
|
|
|
seconds, but these values are unused:
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
@Scheduled(initialDelay = 5000, fixedRate = 5000)
|
|
|
|
public Flux<String> reactiveSomething() {
|
|
|
|
return Flux.just("Hello", "World");
|
|
|
|
}
|
|
|
|
----
|
|
|
|
|
2023-06-06 17:29:25 +08:00
|
|
|
If the `Publisher` emits an `onError` signal, it is logged at `WARN` level and recovered.
|
2023-06-05 20:11:39 +08:00
|
|
|
Because of the asynchronous and lazy nature of `Publisher` instances, exceptions are
|
2023-06-06 17:29:25 +08:00
|
|
|
not thrown from the `Runnable` task: this means that the `ErrorHandler` contract is not
|
|
|
|
involved for reactive methods.
|
2023-06-05 20:11:39 +08:00
|
|
|
|
2023-06-06 17:29:25 +08:00
|
|
|
As a result, further scheduled subscription occurs despite the error.
|
Support `@Scheduled` fixedDelay/fixedRate on Publisher-returning methods
This commit adds support for `@Scheduled` annotation on reactive
methods and Kotlin suspending functions.
Reactive methods are methods that return a `Publisher` or a subclass
of `Publisher`. The `ReactiveAdapterRegistry` is used to support many
implementations, such as `Flux`, `Mono`, `Flow`, `Single`, etc.
Methods should not take any argument and published values will be
ignored, as they are already with synchronous support.
This is implemented in `ScheduledAnnotationReactiveSupport`, which
"converts" Publishers to `Runnable`. This strategy keeps track of
active Subscriptions in the `ScheduledAnnotationBeanPostProcessor`,
in order to cancel them all in case of shutdown.
The existing scheduling support for tasks is reused, aligning the
triggering behavior with the existing support: cron, fixedDelay and
fixedRate are all supported strategies.
If the `Publisher` errors, the exception is logged at warn level and
otherwise ignored. As a result new `Runnable` instances will be
created for each execution and scheduling will continue.
The only difference with synchronous support is that error signals
will not be thrown by those `Runnable` tasks and will not be made
available to the `org.springframework.util.ErrorHandler` contract.
This is due to the asynchronous and lazy nature of Publishers.
Closes gh-23533
Closes gh-28515
2023-06-05 18:25:15 +08:00
|
|
|
|
2023-06-06 17:29:25 +08:00
|
|
|
In the following example, the `Mono` subscription fails twice in the first five seconds.
|
|
|
|
Then subscriptions start succeeding, printing a message to the standard output every five
|
Support `@Scheduled` fixedDelay/fixedRate on Publisher-returning methods
This commit adds support for `@Scheduled` annotation on reactive
methods and Kotlin suspending functions.
Reactive methods are methods that return a `Publisher` or a subclass
of `Publisher`. The `ReactiveAdapterRegistry` is used to support many
implementations, such as `Flux`, `Mono`, `Flow`, `Single`, etc.
Methods should not take any argument and published values will be
ignored, as they are already with synchronous support.
This is implemented in `ScheduledAnnotationReactiveSupport`, which
"converts" Publishers to `Runnable`. This strategy keeps track of
active Subscriptions in the `ScheduledAnnotationBeanPostProcessor`,
in order to cancel them all in case of shutdown.
The existing scheduling support for tasks is reused, aligning the
triggering behavior with the existing support: cron, fixedDelay and
fixedRate are all supported strategies.
If the `Publisher` errors, the exception is logged at warn level and
otherwise ignored. As a result new `Runnable` instances will be
created for each execution and scheduling will continue.
The only difference with synchronous support is that error signals
will not be thrown by those `Runnable` tasks and will not be made
available to the `org.springframework.util.ErrorHandler` contract.
This is due to the asynchronous and lazy nature of Publishers.
Closes gh-23533
Closes gh-28515
2023-06-05 18:25:15 +08:00
|
|
|
seconds:
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
@Scheduled(initialDelay = 0, fixedRate = 5000)
|
|
|
|
public Mono<Void> reactiveSomething() {
|
|
|
|
AtomicInteger countdown = new AtomicInteger(2);
|
|
|
|
|
|
|
|
return Mono.defer(() -> {
|
|
|
|
if (countDown.get() == 0 || countDown.decrementAndGet() == 0) {
|
|
|
|
return Mono.fromRunnable(() -> System.out.println("Message"));
|
|
|
|
}
|
|
|
|
return Mono.error(new IllegalStateException("Cannot deliver message"));
|
|
|
|
})
|
|
|
|
}
|
|
|
|
----
|
|
|
|
|
|
|
|
[NOTE]
|
|
|
|
====
|
2023-06-06 17:29:25 +08:00
|
|
|
When destroying the annotated bean or closing the application context, Spring Framework cancels
|
Support `@Scheduled` fixedDelay/fixedRate on Publisher-returning methods
This commit adds support for `@Scheduled` annotation on reactive
methods and Kotlin suspending functions.
Reactive methods are methods that return a `Publisher` or a subclass
of `Publisher`. The `ReactiveAdapterRegistry` is used to support many
implementations, such as `Flux`, `Mono`, `Flow`, `Single`, etc.
Methods should not take any argument and published values will be
ignored, as they are already with synchronous support.
This is implemented in `ScheduledAnnotationReactiveSupport`, which
"converts" Publishers to `Runnable`. This strategy keeps track of
active Subscriptions in the `ScheduledAnnotationBeanPostProcessor`,
in order to cancel them all in case of shutdown.
The existing scheduling support for tasks is reused, aligning the
triggering behavior with the existing support: cron, fixedDelay and
fixedRate are all supported strategies.
If the `Publisher` errors, the exception is logged at warn level and
otherwise ignored. As a result new `Runnable` instances will be
created for each execution and scheduling will continue.
The only difference with synchronous support is that error signals
will not be thrown by those `Runnable` tasks and will not be made
available to the `org.springframework.util.ErrorHandler` contract.
This is due to the asynchronous and lazy nature of Publishers.
Closes gh-23533
Closes gh-28515
2023-06-05 18:25:15 +08:00
|
|
|
scheduled tasks, which includes the next scheduled subscription to the `Publisher` as well
|
2024-09-26 20:03:46 +08:00
|
|
|
as any past subscription that is still currently active (for example, for long-running publishers
|
Support `@Scheduled` fixedDelay/fixedRate on Publisher-returning methods
This commit adds support for `@Scheduled` annotation on reactive
methods and Kotlin suspending functions.
Reactive methods are methods that return a `Publisher` or a subclass
of `Publisher`. The `ReactiveAdapterRegistry` is used to support many
implementations, such as `Flux`, `Mono`, `Flow`, `Single`, etc.
Methods should not take any argument and published values will be
ignored, as they are already with synchronous support.
This is implemented in `ScheduledAnnotationReactiveSupport`, which
"converts" Publishers to `Runnable`. This strategy keeps track of
active Subscriptions in the `ScheduledAnnotationBeanPostProcessor`,
in order to cancel them all in case of shutdown.
The existing scheduling support for tasks is reused, aligning the
triggering behavior with the existing support: cron, fixedDelay and
fixedRate are all supported strategies.
If the `Publisher` errors, the exception is logged at warn level and
otherwise ignored. As a result new `Runnable` instances will be
created for each execution and scheduling will continue.
The only difference with synchronous support is that error signals
will not be thrown by those `Runnable` tasks and will not be made
available to the `org.springframework.util.ErrorHandler` contract.
This is due to the asynchronous and lazy nature of Publishers.
Closes gh-23533
Closes gh-28515
2023-06-05 18:25:15 +08:00
|
|
|
or even infinite publishers).
|
|
|
|
====
|
|
|
|
|
2022-11-26 00:33:07 +08:00
|
|
|
|
|
|
|
[[scheduling-annotation-support-async]]
|
|
|
|
=== The `@Async` annotation
|
|
|
|
|
|
|
|
You can provide the `@Async` annotation on a method so that invocation of that method
|
|
|
|
occurs asynchronously. In other words, the caller returns immediately upon
|
|
|
|
invocation, while the actual execution of the method occurs in a task that has been
|
|
|
|
submitted to a Spring `TaskExecutor`. In the simplest case, you can apply the annotation
|
|
|
|
to a method that returns `void`, as the following example shows:
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
@Async
|
|
|
|
void doSomething() {
|
|
|
|
// this will be run asynchronously
|
|
|
|
}
|
|
|
|
----
|
|
|
|
|
|
|
|
Unlike the methods annotated with the `@Scheduled` annotation, these methods can expect
|
|
|
|
arguments, because they are invoked in the "`normal`" way by callers at runtime rather
|
2023-09-11 23:36:57 +08:00
|
|
|
than from a scheduled task being managed by the container. For example, the following
|
|
|
|
code is a legitimate application of the `@Async` annotation:
|
2022-11-26 00:33:07 +08:00
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
@Async
|
|
|
|
void doSomething(String s) {
|
|
|
|
// this will be run asynchronously
|
|
|
|
}
|
|
|
|
----
|
|
|
|
|
|
|
|
Even methods that return a value can be invoked asynchronously. However, such methods
|
|
|
|
are required to have a `Future`-typed return value. This still provides the benefit of
|
|
|
|
asynchronous execution so that the caller can perform other tasks prior to calling
|
|
|
|
`get()` on that `Future`. The following example shows how to use `@Async` on a method
|
|
|
|
that returns a value:
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
@Async
|
|
|
|
Future<String> returnSomething(int i) {
|
|
|
|
// this will be run asynchronously
|
|
|
|
}
|
|
|
|
----
|
|
|
|
|
2023-09-11 23:36:57 +08:00
|
|
|
TIP: `@Async` methods may not only declare a regular `java.util.concurrent.Future` return
|
|
|
|
type but also Spring's `org.springframework.util.concurrent.ListenableFuture` or, as of
|
|
|
|
Spring 4.2, JDK 8's `java.util.concurrent.CompletableFuture`, for richer interaction with
|
|
|
|
the asynchronous task and for immediate composition with further processing steps.
|
2022-11-26 00:33:07 +08:00
|
|
|
|
2023-09-11 23:36:57 +08:00
|
|
|
You can not use `@Async` in conjunction with lifecycle callbacks such as `@PostConstruct`.
|
|
|
|
To asynchronously initialize Spring beans, you currently have to use a separate
|
|
|
|
initializing Spring bean that then invokes the `@Async` annotated method on the target,
|
|
|
|
as the following example shows:
|
2022-11-26 00:33:07 +08:00
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
public class SampleBeanImpl implements SampleBean {
|
|
|
|
|
|
|
|
@Async
|
|
|
|
void doSomething() {
|
|
|
|
// ...
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public class SampleBeanInitializer {
|
|
|
|
|
|
|
|
private final SampleBean bean;
|
|
|
|
|
|
|
|
public SampleBeanInitializer(SampleBean bean) {
|
|
|
|
this.bean = bean;
|
|
|
|
}
|
|
|
|
|
|
|
|
@PostConstruct
|
|
|
|
public void initialize() {
|
|
|
|
bean.doSomething();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
----
|
|
|
|
|
|
|
|
NOTE: There is no direct XML equivalent for `@Async`, since such methods should be designed
|
|
|
|
for asynchronous execution in the first place, not externally re-declared to be asynchronous.
|
|
|
|
However, you can manually set up Spring's `AsyncExecutionInterceptor` with Spring AOP,
|
|
|
|
in combination with a custom pointcut.
|
|
|
|
|
|
|
|
|
|
|
|
[[scheduling-annotation-support-qualification]]
|
|
|
|
=== Executor Qualification with `@Async`
|
|
|
|
|
|
|
|
By default, when specifying `@Async` on a method, the executor that is used is the
|
2023-04-19 23:26:17 +08:00
|
|
|
one xref:integration/scheduling.adoc#scheduling-enable-annotation-support[configured when enabling async support],
|
2022-11-26 00:33:07 +08:00
|
|
|
i.e. the "`annotation-driven`" element if you are using XML or your `AsyncConfigurer`
|
|
|
|
implementation, if any. However, you can use the `value` attribute of the `@Async`
|
|
|
|
annotation when you need to indicate that an executor other than the default should be
|
|
|
|
used when executing a given method. The following example shows how to do so:
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
@Async("otherExecutor")
|
|
|
|
void doSomething(String s) {
|
|
|
|
// this will be run asynchronously by "otherExecutor"
|
|
|
|
}
|
|
|
|
----
|
|
|
|
|
|
|
|
In this case, `"otherExecutor"` can be the name of any `Executor` bean in the Spring
|
2023-09-11 23:36:57 +08:00
|
|
|
container, or it may be the name of a qualifier associated with any `Executor` (for example,
|
|
|
|
as specified with the `<qualifier>` element or Spring's `@Qualifier` annotation).
|
2022-11-26 00:33:07 +08:00
|
|
|
|
|
|
|
|
|
|
|
[[scheduling-annotation-support-exception]]
|
|
|
|
=== Exception Management with `@Async`
|
|
|
|
|
|
|
|
When an `@Async` method has a `Future`-typed return value, it is easy to manage
|
|
|
|
an exception that was thrown during the method execution, as this exception is
|
|
|
|
thrown when calling `get` on the `Future` result. With a `void` return type,
|
|
|
|
however, the exception is uncaught and cannot be transmitted. You can provide an
|
|
|
|
`AsyncUncaughtExceptionHandler` to handle such exceptions. The following example shows
|
|
|
|
how to do so:
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
public class MyAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
|
|
|
|
// handle exception
|
|
|
|
}
|
|
|
|
}
|
|
|
|
----
|
|
|
|
|
|
|
|
By default, the exception is merely logged. You can define a custom `AsyncUncaughtExceptionHandler`
|
|
|
|
by using `AsyncConfigurer` or the `<task:annotation-driven/>` XML element.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[scheduling-task-namespace]]
|
|
|
|
== The `task` Namespace
|
|
|
|
|
|
|
|
As of version 3.0, Spring includes an XML namespace for configuring `TaskExecutor` and
|
|
|
|
`TaskScheduler` instances. It also provides a convenient way to configure tasks to be
|
|
|
|
scheduled with a trigger.
|
|
|
|
|
|
|
|
|
|
|
|
[[scheduling-task-namespace-scheduler]]
|
2024-10-15 20:05:54 +08:00
|
|
|
=== The `scheduler` Element
|
2022-11-26 00:33:07 +08:00
|
|
|
|
|
|
|
The following element creates a `ThreadPoolTaskScheduler` instance with the
|
|
|
|
specified thread pool size:
|
|
|
|
|
|
|
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
<task:scheduler id="scheduler" pool-size="10"/>
|
|
|
|
----
|
|
|
|
|
|
|
|
The value provided for the `id` attribute is used as the prefix for thread names
|
|
|
|
within the pool. The `scheduler` element is relatively straightforward. If you do not
|
|
|
|
provide a `pool-size` attribute, the default thread pool has only a single thread.
|
|
|
|
There are no other configuration options for the scheduler.
|
|
|
|
|
|
|
|
|
|
|
|
[[scheduling-task-namespace-executor]]
|
|
|
|
=== The `executor` Element
|
|
|
|
|
|
|
|
The following creates a `ThreadPoolTaskExecutor` instance:
|
|
|
|
|
|
|
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
<task:executor id="executor" pool-size="10"/>
|
|
|
|
----
|
|
|
|
|
2023-04-19 23:26:17 +08:00
|
|
|
As with the scheduler shown in the xref:integration/scheduling.adoc#scheduling-task-namespace-scheduler[previous section],
|
2022-11-26 00:33:07 +08:00
|
|
|
the value provided for the `id` attribute is used as the prefix for thread names within
|
|
|
|
the pool. As far as the pool size is concerned, the `executor` element supports more
|
|
|
|
configuration options than the `scheduler` element. For one thing, the thread pool for
|
|
|
|
a `ThreadPoolTaskExecutor` is itself more configurable. Rather than only a single size,
|
|
|
|
an executor's thread pool can have different values for the core and the max size.
|
|
|
|
If you provide a single value, the executor has a fixed-size thread pool (the core and
|
|
|
|
max sizes are the same). However, the `executor` element's `pool-size` attribute also
|
|
|
|
accepts a range in the form of `min-max`. The following example sets a minimum value of
|
|
|
|
`5` and a maximum value of `25`:
|
|
|
|
|
|
|
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
<task:executor
|
|
|
|
id="executorWithPoolSizeRange"
|
|
|
|
pool-size="5-25"
|
|
|
|
queue-capacity="100"/>
|
|
|
|
----
|
|
|
|
|
|
|
|
In the preceding configuration, a `queue-capacity` value has also been provided.
|
|
|
|
The configuration of the thread pool should also be considered in light of the
|
|
|
|
executor's queue capacity. For the full description of the relationship between pool
|
|
|
|
size and queue capacity, see the documentation for
|
2023-11-21 22:59:24 +08:00
|
|
|
{java-api}/java.base/java/util/concurrent/ThreadPoolExecutor.html[`ThreadPoolExecutor`].
|
2022-11-26 00:33:07 +08:00
|
|
|
The main idea is that, when a task is submitted, the executor first tries to use a
|
|
|
|
free thread if the number of active threads is currently less than the core size.
|
|
|
|
If the core size has been reached, the task is added to the queue, as long as its
|
|
|
|
capacity has not yet been reached. Only then, if the queue's capacity has been
|
|
|
|
reached, does the executor create a new thread beyond the core size. If the max size
|
|
|
|
has also been reached, then the executor rejects the task.
|
|
|
|
|
|
|
|
By default, the queue is unbounded, but this is rarely the desired configuration,
|
2024-10-14 23:57:43 +08:00
|
|
|
because it can lead to `OutOfMemoryError` if enough tasks are added to that queue while
|
2022-11-26 00:33:07 +08:00
|
|
|
all pool threads are busy. Furthermore, if the queue is unbounded, the max size has
|
|
|
|
no effect at all. Since the executor always tries the queue before creating a new
|
|
|
|
thread beyond the core size, a queue must have a finite capacity for the thread pool to
|
|
|
|
grow beyond the core size (this is why a fixed-size pool is the only sensible case
|
|
|
|
when using an unbounded queue).
|
|
|
|
|
|
|
|
Consider the case, as mentioned above, when a task is rejected. By default, when a
|
|
|
|
task is rejected, a thread pool executor throws a `TaskRejectedException`. However,
|
|
|
|
the rejection policy is actually configurable. The exception is thrown when using
|
|
|
|
the default rejection policy, which is the `AbortPolicy` implementation.
|
|
|
|
For applications where some tasks can be skipped under heavy load, you can instead
|
|
|
|
configure either `DiscardPolicy` or `DiscardOldestPolicy`. Another option that works
|
|
|
|
well for applications that need to throttle the submitted tasks under heavy load is
|
|
|
|
the `CallerRunsPolicy`. Instead of throwing an exception or discarding tasks,
|
|
|
|
that policy forces the thread that is calling the submit method to run the task itself.
|
|
|
|
The idea is that such a caller is busy while running that task and not able to submit
|
|
|
|
other tasks immediately. Therefore, it provides a simple way to throttle the incoming
|
|
|
|
load while maintaining the limits of the thread pool and queue. Typically, this allows
|
|
|
|
the executor to "`catch up`" on the tasks it is handling and thereby frees up some
|
|
|
|
capacity on the queue, in the pool, or both. You can choose any of these options from an
|
|
|
|
enumeration of values available for the `rejection-policy` attribute on the `executor`
|
|
|
|
element.
|
|
|
|
|
|
|
|
The following example shows an `executor` element with a number of attributes to specify
|
|
|
|
various behaviors:
|
|
|
|
|
|
|
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
<task:executor
|
|
|
|
id="executorWithCallerRunsPolicy"
|
|
|
|
pool-size="5-25"
|
|
|
|
queue-capacity="100"
|
|
|
|
rejection-policy="CALLER_RUNS"/>
|
|
|
|
----
|
|
|
|
|
|
|
|
Finally, the `keep-alive` setting determines the time limit (in seconds) for which threads
|
|
|
|
may remain idle before being stopped. If there are more than the core number of threads
|
|
|
|
currently in the pool, after waiting this amount of time without processing a task, excess
|
|
|
|
threads get stopped. A time value of zero causes excess threads to stop
|
|
|
|
immediately after executing a task without remaining follow-up work in the task queue.
|
|
|
|
The following example sets the `keep-alive` value to two minutes:
|
|
|
|
|
|
|
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
<task:executor
|
|
|
|
id="executorWithKeepAlive"
|
|
|
|
pool-size="5-25"
|
|
|
|
keep-alive="120"/>
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
|
|
[[scheduling-task-namespace-scheduled-tasks]]
|
2024-10-15 20:05:54 +08:00
|
|
|
=== The `scheduled-tasks` Element
|
2022-11-26 00:33:07 +08:00
|
|
|
|
|
|
|
The most powerful feature of Spring's task namespace is the support for configuring
|
|
|
|
tasks to be scheduled within a Spring Application Context. This follows an approach
|
|
|
|
similar to other "`method-invokers`" in Spring, such as that provided by the JMS namespace
|
|
|
|
for configuring message-driven POJOs. Basically, a `ref` attribute can point to any
|
|
|
|
Spring-managed object, and the `method` attribute provides the name of a method to be
|
|
|
|
invoked on that object. The following listing shows a simple example:
|
|
|
|
|
|
|
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
<task:scheduled-tasks scheduler="myScheduler">
|
|
|
|
<task:scheduled ref="beanA" method="methodA" fixed-delay="5000"/>
|
|
|
|
</task:scheduled-tasks>
|
|
|
|
|
|
|
|
<task:scheduler id="myScheduler" pool-size="10"/>
|
|
|
|
----
|
|
|
|
|
|
|
|
The scheduler is referenced by the outer element, and each individual
|
2023-09-11 23:36:57 +08:00
|
|
|
task includes the configuration of its trigger metadata. In the preceding example,
|
|
|
|
that metadata defines a periodic trigger with a fixed delay indicating the number of
|
2022-11-26 00:33:07 +08:00
|
|
|
milliseconds to wait after each task execution has completed. Another option is
|
|
|
|
`fixed-rate`, indicating how often the method should be run regardless of how long
|
2023-09-11 23:36:57 +08:00
|
|
|
any previous execution takes. Additionally, for both `fixed-delay` and `fixed-rate`
|
|
|
|
tasks, you can specify an 'initial-delay' parameter, indicating the number of
|
|
|
|
milliseconds to wait before the first execution of the method. For more control,
|
|
|
|
you can instead provide a `cron` attribute to provide a
|
|
|
|
xref:integration/scheduling.adoc#scheduling-cron-expression[cron expression].
|
2022-11-26 00:33:07 +08:00
|
|
|
The following example shows these other options:
|
|
|
|
|
|
|
|
[source,xml,indent=0]
|
|
|
|
[subs="verbatim"]
|
|
|
|
----
|
|
|
|
<task:scheduled-tasks scheduler="myScheduler">
|
|
|
|
<task:scheduled ref="beanA" method="methodA" fixed-delay="5000" initial-delay="1000"/>
|
|
|
|
<task:scheduled ref="beanB" method="methodB" fixed-rate="5000"/>
|
|
|
|
<task:scheduled ref="beanC" method="methodC" cron="*/5 * * * * MON-FRI"/>
|
|
|
|
</task:scheduled-tasks>
|
|
|
|
|
|
|
|
<task:scheduler id="myScheduler" pool-size="10"/>
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[scheduling-cron-expression]]
|
|
|
|
== Cron Expressions
|
|
|
|
|
|
|
|
All Spring cron expressions have to conform to the same format, whether you are using them in
|
2023-04-19 23:26:17 +08:00
|
|
|
xref:integration/scheduling.adoc#scheduling-annotation-support-scheduled[`@Scheduled` annotations],
|
|
|
|
xref:integration/scheduling.adoc#scheduling-task-namespace-scheduled-tasks[`task:scheduled-tasks` elements],
|
2023-09-11 23:36:57 +08:00
|
|
|
or someplace else. A well-formed cron expression, such as `* * * * * *`, consists of six
|
|
|
|
space-separated time and date fields, each with its own range of valid values:
|
2022-11-26 00:33:07 +08:00
|
|
|
|
|
|
|
|
|
|
|
....
|
|
|
|
┌───────────── second (0-59)
|
|
|
|
│ ┌───────────── minute (0 - 59)
|
|
|
|
│ │ ┌───────────── hour (0 - 23)
|
|
|
|
│ │ │ ┌───────────── day of the month (1 - 31)
|
|
|
|
│ │ │ │ ┌───────────── month (1 - 12) (or JAN-DEC)
|
|
|
|
│ │ │ │ │ ┌───────────── day of the week (0 - 7)
|
|
|
|
│ │ │ │ │ │ (0 or 7 is Sunday, or MON-SUN)
|
|
|
|
│ │ │ │ │ │
|
|
|
|
* * * * * *
|
|
|
|
....
|
|
|
|
|
|
|
|
There are some rules that apply:
|
|
|
|
|
|
|
|
* A field may be an asterisk (`*`), which always stands for "`first-last`".
|
|
|
|
For the day-of-the-month or day-of-the-week fields, a question mark (`?`) may be used instead of an
|
|
|
|
asterisk.
|
|
|
|
* Commas (`,`) are used to separate items of a list.
|
|
|
|
* Two numbers separated with a hyphen (`-`) express a range of numbers.
|
|
|
|
The specified range is inclusive.
|
|
|
|
* Following a range (or `*`) with `/` specifies the interval of the number's value through the range.
|
|
|
|
* English names can also be used for the month and day-of-week fields.
|
|
|
|
Use the first three letters of the particular day or month (case does not matter).
|
2022-12-01 23:14:36 +08:00
|
|
|
* The day-of-month and day-of-week fields can contain an `L` character, which has a different meaning.
|
2022-11-26 00:33:07 +08:00
|
|
|
** In the day-of-month field, `L` stands for _the last day of the month_.
|
|
|
|
If followed by a negative offset (that is, `L-n`), it means _``n``th-to-last day of the month_.
|
|
|
|
** In the day-of-week field, `L` stands for _the last day of the week_.
|
|
|
|
If prefixed by a number or three-letter name (`dL` or `DDDL`), it means _the last day of week (`d`
|
|
|
|
or `DDD`) in the month_.
|
|
|
|
* The day-of-month field can be `nW`, which stands for _the nearest weekday to day of the month ``n``_.
|
|
|
|
If `n` falls on Saturday, this yields the Friday before it.
|
|
|
|
If `n` falls on Sunday, this yields the Monday after, which also happens if `n` is `1` and falls on
|
|
|
|
a Saturday (that is: `1W` stands for _the first weekday of the month_).
|
|
|
|
* If the day-of-month field is `LW`, it means _the last weekday of the month_.
|
|
|
|
* The day-of-week field can be `d#n` (or `DDD#n`), which stands for _the ``n``th day of week `d`
|
|
|
|
(or ``DDD``) in the month_.
|
|
|
|
|
|
|
|
Here are some examples:
|
|
|
|
|
|
|
|
|===
|
|
|
|
| Cron Expression | Meaning
|
|
|
|
|
|
|
|
|`0 0 * * * *` | top of every hour of every day
|
|
|
|
|`*/10 * * * * *` | every ten seconds
|
|
|
|
| `0 0 8-10 * * *` | 8, 9 and 10 o'clock of every day
|
|
|
|
| `0 0 6,19 * * *` | 6:00 AM and 7:00 PM every day
|
|
|
|
| `0 0/30 8-10 * * *` | 8:00, 8:30, 9:00, 9:30, 10:00 and 10:30 every day
|
|
|
|
| `0 0 9-17 * * MON-FRI`| on the hour nine-to-five weekdays
|
|
|
|
| `0 0 0 25 DEC ?` | every Christmas Day at midnight
|
|
|
|
| `0 0 0 L * *` | last day of the month at midnight
|
|
|
|
| `0 0 0 L-3 * *` | third-to-last day of the month at midnight
|
|
|
|
| `0 0 0 * * 5L` | last Friday of the month at midnight
|
|
|
|
| `0 0 0 * * THUL` | last Thursday of the month at midnight
|
|
|
|
| `0 0 0 1W * *` | first weekday of the month at midnight
|
|
|
|
| `0 0 0 LW * *` | last weekday of the month at midnight
|
|
|
|
| `0 0 0 ? * 5#2` | the second Friday in the month at midnight
|
|
|
|
| `0 0 0 ? * MON#1` | the first Monday in the month at midnight
|
|
|
|
|===
|
|
|
|
|
2023-04-19 23:23:59 +08:00
|
|
|
[[macros]]
|
2022-11-26 00:33:07 +08:00
|
|
|
=== Macros
|
|
|
|
|
2023-09-11 23:36:57 +08:00
|
|
|
Expressions such as `0 0 * * * *` are hard for humans to parse and are, therefore,
|
|
|
|
hard to fix in case of bugs. To improve readability, Spring supports the following
|
|
|
|
macros, which represent commonly used sequences. You can use these macros instead
|
|
|
|
of the six-digit value, thus: `@Scheduled(cron = "@hourly")`.
|
2022-11-26 00:33:07 +08:00
|
|
|
|
|
|
|
|===
|
|
|
|
|Macro | Meaning
|
|
|
|
|
|
|
|
| `@yearly` (or `@annually`) | once a year (`0 0 0 1 1 *`)
|
|
|
|
| `@monthly` | once a month (`0 0 0 1 * *`)
|
|
|
|
| `@weekly` | once a week (`0 0 0 * * 0`)
|
|
|
|
| `@daily` (or `@midnight`) | once a day (`0 0 0 * * *`), or
|
|
|
|
| `@hourly` | once an hour, (`0 0 * * * *`)
|
|
|
|
|===
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[scheduling-quartz]]
|
|
|
|
== Using the Quartz Scheduler
|
|
|
|
|
2023-09-11 23:36:57 +08:00
|
|
|
Quartz uses `Trigger`, `Job`, and `JobDetail` objects to realize scheduling of all
|
|
|
|
kinds of jobs. For the basic concepts behind Quartz, see the
|
2022-12-18 20:20:46 +08:00
|
|
|
https://www.quartz-scheduler.org/[Quartz Web site]. For convenience purposes, Spring
|
|
|
|
offers a couple of classes that simplify using Quartz within Spring-based applications.
|
2022-11-26 00:33:07 +08:00
|
|
|
|
|
|
|
|
|
|
|
[[scheduling-quartz-jobdetail]]
|
|
|
|
=== Using the `JobDetailFactoryBean`
|
|
|
|
|
2023-09-11 23:36:57 +08:00
|
|
|
Quartz `JobDetail` objects contain all the information needed to run a job. Spring
|
|
|
|
provides a `JobDetailFactoryBean`, which provides bean-style properties for XML
|
|
|
|
configuration purposes. Consider the following example:
|
2022-11-26 00:33:07 +08:00
|
|
|
|
|
|
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
<bean name="exampleJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
|
|
|
|
<property name="jobClass" value="example.ExampleJob"/>
|
|
|
|
<property name="jobDataAsMap">
|
|
|
|
<map>
|
|
|
|
<entry key="timeout" value="5"/>
|
|
|
|
</map>
|
|
|
|
</property>
|
|
|
|
</bean>
|
|
|
|
----
|
|
|
|
|
|
|
|
The job detail configuration has all the information it needs to run the job (`ExampleJob`).
|
|
|
|
The timeout is specified in the job data map. The job data map is available through the
|
|
|
|
`JobExecutionContext` (passed to you at execution time), but the `JobDetail` also gets
|
2023-09-11 23:36:57 +08:00
|
|
|
its properties from the job data mapped to properties of the job instance. So, in the
|
|
|
|
following example, the `ExampleJob` contains a bean property named `timeout`, and the
|
|
|
|
`JobDetail` has it applied automatically:
|
2022-11-26 00:33:07 +08:00
|
|
|
|
2023-02-22 19:49:20 +08:00
|
|
|
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
|
2022-11-26 00:33:07 +08:00
|
|
|
----
|
|
|
|
package example;
|
|
|
|
|
|
|
|
public class ExampleJob extends QuartzJobBean {
|
|
|
|
|
|
|
|
private int timeout;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Setter called after the ExampleJob is instantiated
|
2023-02-22 19:48:40 +08:00
|
|
|
* with the value from the JobDetailFactoryBean.
|
2022-11-26 00:33:07 +08:00
|
|
|
*/
|
|
|
|
public void setTimeout(int timeout) {
|
|
|
|
this.timeout = timeout;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void executeInternal(JobExecutionContext ctx) throws JobExecutionException {
|
|
|
|
// do the actual work
|
|
|
|
}
|
|
|
|
}
|
|
|
|
----
|
|
|
|
|
|
|
|
All additional properties from the job data map are available to you as well.
|
|
|
|
|
|
|
|
NOTE: By using the `name` and `group` properties, you can modify the name and the group
|
|
|
|
of the job, respectively. By default, the name of the job matches the bean name
|
|
|
|
of the `JobDetailFactoryBean` (`exampleJob` in the preceding example above).
|
|
|
|
|
|
|
|
|
|
|
|
[[scheduling-quartz-method-invoking-job]]
|
|
|
|
=== Using the `MethodInvokingJobDetailFactoryBean`
|
|
|
|
|
|
|
|
Often you merely need to invoke a method on a specific object. By using the
|
|
|
|
`MethodInvokingJobDetailFactoryBean`, you can do exactly this, as the following example shows:
|
|
|
|
|
|
|
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
|
|
|
|
<property name="targetObject" ref="exampleBusinessObject"/>
|
|
|
|
<property name="targetMethod" value="doIt"/>
|
|
|
|
</bean>
|
|
|
|
----
|
|
|
|
|
|
|
|
The preceding example results in the `doIt` method being called on the
|
|
|
|
`exampleBusinessObject` method, as the following example shows:
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
public class ExampleBusinessObject {
|
|
|
|
|
|
|
|
// properties and collaborators
|
|
|
|
|
|
|
|
public void doIt() {
|
|
|
|
// do the actual work
|
|
|
|
}
|
|
|
|
}
|
|
|
|
----
|
|
|
|
|
|
|
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
<bean id="exampleBusinessObject" class="examples.ExampleBusinessObject"/>
|
|
|
|
----
|
|
|
|
|
|
|
|
By using the `MethodInvokingJobDetailFactoryBean`, you need not create one-line jobs
|
|
|
|
that merely invoke a method. You need only create the actual business object and
|
|
|
|
wire up the detail object.
|
|
|
|
|
|
|
|
By default, Quartz Jobs are stateless, resulting in the possibility of jobs interfering
|
2022-12-18 20:20:46 +08:00
|
|
|
with each other. If you specify two triggers for the same `JobDetail`, it is possible
|
|
|
|
that the second one starts before the first job has finished. If `JobDetail` classes
|
|
|
|
implement the `Stateful` interface, this does not happen: the second job does not start
|
|
|
|
before the first one has finished.
|
|
|
|
|
|
|
|
To make jobs resulting from the `MethodInvokingJobDetailFactoryBean` be non-concurrent,
|
|
|
|
set the `concurrent` flag to `false`, as the following example shows:
|
2022-11-26 00:33:07 +08:00
|
|
|
|
|
|
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
|
|
|
|
<property name="targetObject" ref="exampleBusinessObject"/>
|
|
|
|
<property name="targetMethod" value="doIt"/>
|
|
|
|
<property name="concurrent" value="false"/>
|
|
|
|
</bean>
|
|
|
|
----
|
|
|
|
|
|
|
|
NOTE: By default, jobs will run in a concurrent fashion.
|
|
|
|
|
|
|
|
|
|
|
|
[[scheduling-quartz-cron]]
|
|
|
|
=== Wiring up Jobs by Using Triggers and `SchedulerFactoryBean`
|
|
|
|
|
2023-09-11 23:36:57 +08:00
|
|
|
We have created job details and jobs. We have also reviewed the convenience bean that
|
|
|
|
lets you invoke a method on a specific object. Of course, we still need to schedule the
|
2022-11-26 00:33:07 +08:00
|
|
|
jobs themselves. This is done by using triggers and a `SchedulerFactoryBean`. Several
|
|
|
|
triggers are available within Quartz, and Spring offers two Quartz `FactoryBean`
|
|
|
|
implementations with convenient defaults: `CronTriggerFactoryBean` and
|
|
|
|
`SimpleTriggerFactoryBean`.
|
|
|
|
|
|
|
|
Triggers need to be scheduled. Spring offers a `SchedulerFactoryBean` that exposes
|
|
|
|
triggers to be set as properties. `SchedulerFactoryBean` schedules the actual jobs with
|
|
|
|
those triggers.
|
|
|
|
|
|
|
|
The following listing uses both a `SimpleTriggerFactoryBean` and a `CronTriggerFactoryBean`:
|
|
|
|
|
|
|
|
[source,xml,indent=0]
|
|
|
|
[subs="verbatim"]
|
|
|
|
----
|
|
|
|
<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
|
|
|
|
<!-- see the example of method invoking job above -->
|
|
|
|
<property name="jobDetail" ref="jobDetail"/>
|
|
|
|
<!-- 10 seconds -->
|
|
|
|
<property name="startDelay" value="10000"/>
|
|
|
|
<!-- repeat every 50 seconds -->
|
|
|
|
<property name="repeatInterval" value="50000"/>
|
|
|
|
</bean>
|
|
|
|
|
|
|
|
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
|
|
|
|
<property name="jobDetail" ref="exampleJob"/>
|
|
|
|
<!-- run every morning at 6 AM -->
|
|
|
|
<property name="cronExpression" value="0 0 6 * * ?"/>
|
|
|
|
</bean>
|
|
|
|
----
|
|
|
|
|
2023-09-11 23:36:57 +08:00
|
|
|
The preceding example sets up two triggers, one running every 50 seconds with a starting
|
|
|
|
delay of 10 seconds and one running every morning at 6 AM. To finalize everything,
|
|
|
|
we need to set up the `SchedulerFactoryBean`, as the following example shows:
|
2022-11-26 00:33:07 +08:00
|
|
|
|
|
|
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
|
|
|
|
<property name="triggers">
|
|
|
|
<list>
|
|
|
|
<ref bean="cronTrigger"/>
|
|
|
|
<ref bean="simpleTrigger"/>
|
|
|
|
</list>
|
|
|
|
</property>
|
|
|
|
</bean>
|
|
|
|
----
|
|
|
|
|
|
|
|
More properties are available for the `SchedulerFactoryBean`, such as the calendars used by the
|
|
|
|
job details, properties to customize Quartz with, and a Spring-provided JDBC DataSource. See
|
2023-11-21 22:59:24 +08:00
|
|
|
the {spring-framework-api}/scheduling/quartz/SchedulerFactoryBean.html[`SchedulerFactoryBean`]
|
2022-11-26 00:33:07 +08:00
|
|
|
javadoc for more information.
|
|
|
|
|
|
|
|
NOTE: `SchedulerFactoryBean` also recognizes a `quartz.properties` file in the classpath,
|
|
|
|
based on Quartz property keys, as with regular Quartz configuration. Please note that many
|
|
|
|
`SchedulerFactoryBean` settings interact with common Quartz settings in the properties file;
|
|
|
|
it is therefore not recommended to specify values at both levels. For example, do not set
|
|
|
|
an "org.quartz.jobStore.class" property if you mean to rely on a Spring-provided DataSource,
|
|
|
|
or specify an `org.springframework.scheduling.quartz.LocalDataSourceJobStore` variant which
|
|
|
|
is a full-fledged replacement for the standard `org.quartz.impl.jdbcjobstore.JobStoreTX`.
|
|
|
|
|