此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Framework 6.2.4! |
任务执行和调度
Spring Framework 为
任务替换为TaskExecutor
和TaskScheduler
接口。Spring 也
具有支持线程池或委托给
CommonJ 中的请求。最终,这些
公共接口背后的实现抽象出
Java SE 和 Jakarta EE 环境。
Spring 还具有集成类,以支持使用 Quartz Scheduler 进行调度。
SpringTaskExecutor
抽象化
Executors 是线程池概念的 JDK 名称。“executor” 命名是 由于无法保证底层实现是 实际上是一个游泳池。执行程序可以是单线程的,甚至可以是同步的。Spring的 abstraction 隐藏了 Java SE 和 Jakarta EE 环境之间的实现细节。
Spring的TaskExecutor
interface 与java.util.concurrent.Executor
接口。事实上,最初它存在的主要原因是抽象出来
使用线程池时需要 Java 5。该接口只有一个方法
(execute(Runnable task)
),该 API 接受基于语义执行的任务
以及线程池的配置。
这TaskExecutor
最初是为了给其他 Spring 组件一个抽象而创建的
用于需要的线程池。组件(如ApplicationEventMulticaster
,
JMS 的AbstractMessageListenerContainer
和 Quartz 集成都使用TaskExecutor
抽象到池线程。但是,如果您的 bean 需要线程池
行为,您也可以根据自己的需要使用此抽象。
TaskExecutor
类型
Spring 包含许多预构建的TaskExecutor
.
您很可能永远不需要实现自己的。
Spring 提供的变体如下:
-
SyncTaskExecutor
: 此实现不会异步运行调用。相反,每个 调用发生在调用线程中。它主要用于各种情况 不需要多线程处理,例如在简单的测试用例中。 -
SimpleAsyncTaskExecutor
: 此实现不会重用任何线程。相反,它会启动一个新线程 对于每个调用。但是,它确实支持阻止 在释放槽之前超过限制的任何调用。如果你 正在寻找真正的池化,请参阅ThreadPoolTaskExecutor
,在此列表的后面部分。 -
ConcurrentTaskExecutor
: 此实现是java.util.concurrent.Executor
实例。 还有另一种选择 (ThreadPoolTaskExecutor
) 公开Executor
配置参数作为 Bean 属性。很少需要使用ConcurrentTaskExecutor
径直。但是,如果ThreadPoolTaskExecutor
莫 足够灵活,满足您的需求,ConcurrentTaskExecutor
是一种替代方法。 -
ThreadPoolTaskExecutor
: 此实现是最常用的。它公开了用于配置的 bean 属性 一个java.util.concurrent.ThreadPoolExecutor
并将其包装在TaskExecutor
. 如果您需要适应不同类型的java.util.concurrent.Executor
, 我们建议您使用ConcurrentTaskExecutor
相反。 -
DefaultManagedTaskExecutor
: 此实现使用 JNDI 获取的ManagedExecutorService
在 JSR-236 中 兼容的运行时环境(例如 Jakarta EE 应用程序服务器), 为此,替换 CommonJ WorkManager。
自 6.1 起,ThreadPoolTaskExecutor
提供 pause/resume 功能和 graceful
shutdown 的 Spring 生命周期管理。还有一个新的 “virtualThreads”
选项SimpleAsyncTaskExecutor
它与 JDK 21 的虚拟线程保持一致,
以及SimpleAsyncTaskExecutor
也。
使用TaskExecutor
Spring的TaskExecutor
实现通常与依赖项注入一起使用。
在下面的示例中,我们定义了一个 Bean,它使用ThreadPoolTaskExecutor
要异步打印出一组消息:
import org.springframework.core.task.TaskExecutor;
public class TaskExecutorExample {
private class MessagePrinterTask implements Runnable {
private String message;
public MessagePrinterTask(String message) {
this.message = message;
}
public void run() {
System.out.println(message);
}
}
private TaskExecutor taskExecutor;
public TaskExecutorExample(TaskExecutor taskExecutor) {
this.taskExecutor = taskExecutor;
}
public void printMessages() {
for(int i = 0; i < 25; i++) {
taskExecutor.execute(new MessagePrinterTask("Message" + i));
}
}
}
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:
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="5"/>
<property name="maxPoolSize" value="10"/>
<property name="queueCapacity" value="25"/>
</bean>
<bean id="taskExecutorExample" class="TaskExecutorExample">
<constructor-arg ref="taskExecutor"/>
</bean>
The Spring TaskScheduler
Abstraction
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:
public interface TaskScheduler {
Clock getClock();
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.
Trigger
Interface
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:
public interface Trigger {
Instant nextExecution(TriggerContext triggerContext);
}
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.
public interface TriggerContext {
Clock getClock();
Instant lastScheduledExecution();
Instant lastActualExecution();
Instant lastCompletion();
}
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
cron expressions.
For example, the following task is scheduled to run 15 minutes past each hour but only
during the 9-to-5 "business hours" on weekdays:
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.
Such a component could take advantage of dependency injection so that you can configure
such Triggers
externally and, therefore, easily modify or extend them.
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
application itself. For such scenarios, Spring provides a DefaultManagedTaskScheduler
that delegates to a JSR-236 ManagedScheduledExecutorService
in a Jakarta EE environment.
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.
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
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).
Annotation Support for Scheduling and Asynchronous Execution
Spring provides annotation support for both task scheduling and asynchronous method
execution.
Enable Scheduling Annotations
To enable support for @Scheduled
and @Async
annotations, you can add @EnableScheduling
and @EnableAsync
to one of your @Configuration
classes, as the following example shows:
@Configuration
@EnableAsync
@EnableScheduling
public class AppConfig {
}
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
SchedulingConfigurer
and AsyncConfigurer
javadoc for full details.
If you prefer XML configuration, you can use the <task:annotation-driven>
element,
as the following example shows:
<task:annotation-driven executor="myExecutor" scheduler="myScheduler"/>
<task:executor id="myExecutor" pool-size="5"/>
<task:scheduler id="myScheduler" pool-size="10"/>
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
.
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.
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.
@Scheduled(fixedDelay = 5000)
public void doSomething() {
// something that should run periodically
}
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.
@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
successive start times of each invocation):
@Scheduled(fixedRate = 5, timeUnit = TimeUnit.SECONDS)
public void doSomething() {
// something that should run periodically
}
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:
@Scheduled(initialDelay = 1000, fixedRate = 5000)
public void doSomething() {
// something that should run periodically
}
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:
@Scheduled(initialDelay = 1000)
public void doSomething() {
// something that should run only once
}
If simple periodic scheduling is not expressive enough, you can provide a
cron expression.
The following example runs only on weekdays:
@Scheduled(cron="*/5 * * * * MON-FRI")
public void doSomething() {
// something that should run on weekdays only
}
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.
@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.
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.
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:
@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:
@Scheduled(fixedDelay = 500)
public Single<String> rxjavaNonPublisher() {
return Single.just("example");
}
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:
@Scheduled(fixedDelay = 500)
suspend fun something() {
// do something asynchronous
}
-
methods that return a Kotlin Flow
or Deferred
instance, like in the following example:
@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
suspending functions, the kotlinx.coroutines.reactor
bridge must also be present to allow
the framework to invoke a suspending function as a Publisher
.
The Spring Framework will obtain a Publisher
for the annotated method once and will
schedule a Runnable
in which it subscribes to said Publisher
. These inner regular
subscriptions occur according to the corresponding cron
/fixedDelay
/fixedRate
configuration.
If the Publisher
emits onNext
signal(s), these are ignored and discarded (the same way
return values from synchronous @Scheduled
methods are ignored).
In the following example, the Flux
emits onNext("Hello")
, onNext("World")
every 5
seconds, but these values are unused:
@Scheduled(initialDelay = 5000, fixedRate = 5000)
public Flux<String> reactiveSomething() {
return Flux.just("Hello", "World");
}
If the Publisher
emits an onError
signal, it is logged at WARN
level and recovered.
Because of the asynchronous and lazy nature of Publisher
instances, exceptions are
not thrown from the Runnable
task: this means that the ErrorHandler
contract is not
involved for reactive methods.
As a result, further scheduled subscription occurs despite the error.
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
seconds:
@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"));
})
}
When destroying the annotated bean or closing the application context, Spring Framework cancels
scheduled tasks, which includes the next scheduled subscription to the Publisher
as well
as any past subscription that is still currently active (e.g. for long-running publishers
or even infinite publishers).
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:
@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
than from a scheduled task being managed by the container. For example, the following
code is a legitimate application of the @Async
annotation:
@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:
@Async
Future<String> returnSomething(int i) {
// this will be run asynchronously
}
@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.
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:
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();
}
}
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.
Executor Qualification with @Async
By default, when specifying @Async
on a method, the executor that is used is the
one configured when enabling async support,
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:
@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
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).
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:
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.
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.
The 'scheduler' Element
The following element creates a ThreadPoolTaskScheduler
instance with the
specified thread pool size:
<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.
The executor
Element
The following creates a ThreadPoolTaskExecutor
instance:
<task:executor id="executor" pool-size="10"/>
As with the scheduler shown in the previous section,
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
:
<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
ThreadPoolExecutor
.
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,
because it can lead to OutOfMemoryError
if enough tasks are added to that queue while
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:
<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:
<task:executor
id="executorWithKeepAlive"
pool-size="5-25"
keep-alive="120"/>
The 'scheduled-tasks' Element
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:
<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
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
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
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
cron expression.
The following example shows these other options:
<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"/>
Cron Expressions
All Spring cron expressions have to conform to the same format, whether you are using them in
@Scheduled
annotations,
task:scheduled-tasks
elements,
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:* * * * * *
┌───────────── 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).
-
The day-of-month and day-of-week fields can contain an L
character, which has a different meaning.
-
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
Macros
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")
.
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 * * * *
)
Using the Quartz Scheduler
Quartz uses Trigger
, Job
, and JobDetail
objects to realize scheduling of all
kinds of jobs. For the basic concepts behind Quartz, see the
Quartz Web site. For convenience purposes, Spring
offers a couple of classes that simplify using Quartz within Spring-based applications.
Using the JobDetailFactoryBean
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:
<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
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:
package example;
public class ExampleJob extends QuartzJobBean {
private int timeout;
/**
* Setter called after the ExampleJob is instantiated
* with the value from the JobDetailFactoryBean.
*/
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.
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).
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:
<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:
public class ExampleBusinessObject {
// properties and collaborators
public void doIt() {
// do the actual work
}
}
<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
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:
<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>
By default, jobs will run in a concurrent fashion.
Wiring up Jobs by Using Triggers and SchedulerFactoryBean
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
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
:
<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>
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:
<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
the SchedulerFactoryBean
javadoc for more information.
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
.