Task Execution and Scheduling
-
Trigger
-
CronTrigger
-
TriggerContext
-
Scheduled
-
Async
-
@EnableScheduling
-
@Scheduled
-
@Async
-
task:scheduler
-
task:executor
-
task:scheduled-tasks
Spring 框架提供抽象操作,可通过 TaskExecutor
和 TaskScheduler
接口分别异步执行和计划任务。Spring 还提供对这些接口的实现,支持应用程序服务器环境中的线程池或委派到 CommonJ。最终,在常用接口后面使用这些实现从 Java SE 和 Jakarta EE 环境中抽象出差异。
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
implementations behind the common interfaces abstracts away the differences between
Java SE and Jakarta EE environments.
Spring 还提供集成类来支持使用 Quartz Scheduler 进行计划。
Spring also features integration classes to support scheduling with the Quartz Scheduler.
The Spring TaskExecutor
Abstraction
Executor 是 JDK 中线程池概念的名称。“executor
” 命名归因于一个事实,即无法保证底层实现事实上是一个池。执行器可能是单线程,甚至是同步的。Spring 的抽象层隐藏了 Java SE 和 Jakarta EE 环境中的实现详情。
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 的 TaskExecutor
接口与 java.util.concurrent.Executor
接口相同。实际上,最初,它存在的主要原因是为了在使用线程池时抽象掉对 Java 5 的需求。该接口具有一个方法(execute(Runnable task)
),它基于线程池的语义和配置接受一个任务执行。
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.
TaskExecutor
最初是为了为其他需要线程池的 Spring 组件提供抽象而创建的。诸如 ApplicationEventMulticaster
、JMS 中的 AbstractMessageListenerContainer
和 Quartz 集成的组件都使用 TaskExecutor
抽象来池化线程。然而,如果你的 Bean 需要线程池行为,你也可以将此抽象用于你的自身需求。
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.
TaskExecutor
Types
Spring 包括许多 TaskExecutor
的预构建实现。你很可能永远不需要实现自己的实现。Spring 提供的变体如下:
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, seeThreadPoolTaskExecutor
, later in this list. -
ConcurrentTaskExecutor
: This implementation is an adapter for ajava.util.concurrent.Executor
instance. There is an alternative (ThreadPoolTaskExecutor
) that exposes theExecutor
configuration parameters as bean properties. There is rarely a need to useConcurrentTaskExecutor
directly. However, if theThreadPoolTaskExecutor
is not flexible enough for your needs,ConcurrentTaskExecutor
is an alternative. -
ThreadPoolTaskExecutor
: This implementation is most commonly used. It exposes bean properties for configuring ajava.util.concurrent.ThreadPoolExecutor
and wraps it in aTaskExecutor
. If you need to adapt to a different kind ofjava.util.concurrent.Executor
, we recommend that you use aConcurrentTaskExecutor
instead. -
DefaultManagedTaskExecutor
: This implementation uses a JNDI-obtainedManagedExecutorService
in a JSR-236 compatible runtime environment (such as a Jakarta EE application server), replacing a CommonJ WorkManager for that purpose.
从 6.1 开始,ThreadPoolTaskExecutor
提供了一个暂停/恢复功能,并通过 Spring 的生命周期管理来优雅关闭。SimpleAsyncTaskExecutor
上还有一个新的“virtualThreads”选项,它与 JDK 21 的虚拟线程保持一致,以及为 SimpleAsyncTaskExecutor
提供的优雅关闭功能。
As of 6.1, ThreadPoolTaskExecutor
provides a pause/resume capability and graceful
shutdown through Spring’s lifecycle management. There is also a new "virtualThreads"
option on SimpleAsyncTaskExecutor
which is aligned with JDK 21’s Virtual Threads,
as well as a graceful shutdown capability for SimpleAsyncTaskExecutor
as well.
Using a TaskExecutor
Spring 的 TaskExecutor
实现通常与依赖注入一起使用。在以下示例中,我们定义了一个使用 ThreadPoolTaskExecutor
来异步打印一组消息的 Bean:
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:
-
Java
-
Kotlin
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));
}
}
}
class TaskExecutorExample(private val taskExecutor: TaskExecutor) {
private inner class MessagePrinterTask(private val message: String) : Runnable {
override fun run() {
println(message)
}
}
fun printMessages() {
for (i in 0..24) {
taskExecutor.execute(
MessagePrinterTask(
"Message$i"
)
)
}
}
}
如你所见,不是从池中检索线程并自己执行线程,而是将 Runnable
添加到队列中。然后,TaskExecutor
会使用其内部规则来决定在何时运行任务。
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.
为了配置 TaskExecutor
使用的规则,我们公开了简单的 Bean 属性:
To configure the rules that the TaskExecutor
uses, we expose simple bean properties:
-
Java
-
Kotlin
-
Xml
@Bean
ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(5);
taskExecutor.setMaxPoolSize(10);
taskExecutor.setQueueCapacity(25);
return taskExecutor;
}
@Bean
TaskExecutorExample taskExecutorExample(ThreadPoolTaskExecutor taskExecutor) {
return new TaskExecutorExample(taskExecutor);
}
@Bean
fun taskExecutor() = ThreadPoolTaskExecutor().apply {
corePoolSize = 5
maxPoolSize = 10
queueCapacity = 25
}
@Bean
fun taskExecutorExample(taskExecutor: ThreadPoolTaskExecutor) = TaskExecutorExample(taskExecutor)
<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
除了 TaskExecutor
抽象层外,Spring 还具有 TaskScheduler
SPI,它提供了多种方法来计划任务以便某个时间点运行。以下清单显示了 TaskScheduler
接口定义:
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);
最简单的方法是一个名为 schedule
的方法,它只采用一个 Runnable
和一个 Instant
。该方法会导致任务在指定时间后运行一次。所有其他方法都能够计划任务重复运行。固定速率和固定延迟方法用于简单,周期性执行,但是接受 Trigger
的方法要灵活得多。
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
Trigger
接口基本上受 JSR-236 启发。Trigger
的基本思想是执行时间可能会根据过去的执行结果甚至任意条件来确定。如果这些确定会考虑前一个执行的结果,那么这些信息将在 TriggerContext
中可用。Trigger
接口本身非常简单,如下面的清单所示:
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);
}
TriggerContext
是最重要的部分。它封装了所有相关数据,并且将来如果需要,可以进行扩展。TriggerContext
是一个接口(默认情况下使用 SimpleTriggerContext
实现)。以下清单显示了 Trigger
实现可用的方法。
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 提供了 Trigger
接口的两种实现。最有趣的是 CronTrigger
。它支持基于 cron expressions 的任务计划。例如,以下任务计划在每小时过去 15 分钟后运行,但仅限于工作日的 9 点至 5 点的“工作时间”:
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"));
另一个实现是接受固定周期、可选初始延迟值和一个布尔值来指示 period 应解释为固定速率还是固定延迟的 PeriodicTrigger
。由于 TaskScheduler
接口已定义了用于以固定速率或以固定延迟计划任务的方法,因此应尽可能直接使用这些方法。PeriodicTrigger
实现的价值在于你可以在依赖 Trigger
抽象的组件中使用该实现。例如,允许交替使用周期性触发器、基于 Cron 的触发器,甚至是自定义触发器实现可能很方便。这样的组件可以利用依赖注入,以便你能够在外部配置此类 Triggers
,从而轻松修改或扩展它们。
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
与 Spring 的 TaskExecutor
抽象层一样,TaskScheduler
设置的主要优点在于将应用程序的调度需求与部署环境分离。在将应用程序部署到应用程序服务器的环境中(应用程序本身不应直接创建线程)时,此抽象级别尤为重要。对于此类方案,Spring 提供了一个 DefaultManagedTaskScheduler
,该调度器在 Jakarta EE 环境中委派给 JSR-236 ManagedScheduledExecutorService
。
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.
只要不需要外部线程管理,更简单的替代方案是应用程序中安装一个本地 ScheduledExecutorService
,它可以通过 Spring 的 ConcurrentTaskScheduler
进行调整。为了方便起见,Spring 还提供了一个 ThreadPoolTaskScheduler
,它在内部委派给 ScheduledExecutorService
来提供类似于 ThreadPoolTaskExecutor
的通用 Bean 样式配置。这些变体对于宽松的应用程序服务器环境中的本地嵌入式线程池安装同样非常有效,尤其是在 Tomcat 和 Jetty 上。
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.
从 6.1 开始,ThreadPoolTaskScheduler
通过 Spring 的生命周期管理提供了暂停/恢复功能和优雅的关闭功能。还有一个称为 SimpleAsyncTaskScheduler
的新选项,该选项与 JDK 21 的 Virtual Threads 对齐,它使用单个调度程序线程,但为每个计划的任务执行启动一个新线程(除了固定延迟任务的所有任务都在单个调度程序线程上运行,因此对于此基于虚拟线程的选项,建议使用固定速率和 cron 触发器)。
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 为任务计划和异步方法执行提供注释支持。
Spring provides annotation support for both task scheduling and asynchronous method execution.
Enable Scheduling Annotations
要启用对 @Scheduled
和 @Async
注释的支持,可以将 @EnableScheduling
和 @EnableAsync
添加到您的一个 @Configuration
类,或 <task:annotation-driven>
元素,如下面的示例所示:
To enable support for @Scheduled
and @Async
annotations, you can add @EnableScheduling
and @EnableAsync
to one of your @Configuration
classes, or <task:annotation-driven>
element,
as the following example shows:
-
Java
-
Kotlin
-
Xml
@Configuration
@EnableAsync
@EnableScheduling
public class SchedulingConfiguration {
}
@Configuration
@EnableAsync
@EnableScheduling
class SchedulingConfiguration
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/task
https://www.springframework.org/schema/task/spring-task.xsd">
<task:annotation-driven executor="myExecutor" scheduler="myScheduler"/>
<task:executor id="myExecutor" pool-size="5"/>
<task:scheduler id="myScheduler" pool-size="10"/>
</beans>
你可以挑选并选择与你的应用程序关联的注释。例如,如果你仅需要 @Scheduled
的支持,那么你可以省略 @EnableAsync
。为了进行更精细的控制,你还可以额外地实现 SchedulingConfigurer
接口、AsyncConfigurer
接口或两者。请参见 SchedulingConfigurer
和 AsyncConfigurer
javadoc 以获得详细信息。
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.
请注意,在之前的 XML 中,提供了一个执行程序引用来处理那些与带有 @Async
注释的方法相对应任务,而调度程序引用是用来管理那些用 @Scheduled
注释的方法的。
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 |
The @Scheduled
annotation
您可以将 @Scheduled
注释添加到方法中,以及触发器元数据。例如,以下方法每隔五秒(5000 毫秒)使用固定延迟触发一次,这意味着该周期从每次前一次调用的完成时间开始计算。
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 例如,也可以将前一个示例写成如下形式。 For example, the previous example can also be written as follows.
|
如果您需要固定速率执行,则可以在注释中使用 fixedRate
属性。以下方法每隔五秒(从每次调用的连续开始时间测量)触发一次:
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
}
对于固定延迟和固定速率任务,您可以通过指示在方法的首次执行前等待的时间量来指定初始延迟,如下面的 fixedRate
示例所示:
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
}
如果简单的周期性计划不够明确,你可以提供一个 cron expression。以下示例仅在工作日运行:
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 |
请注意,要调度的那些方法必须具有 void 返回值,并且不得接受任何参数。如果方法需要与应用程序上下文的其他对象进行交互,则通常会通过依赖项注入提供这些对象。
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
可用作可重复注释。如果在同一个方法上找到多个计划声明,则会独立处理它们中的每一个,并为它们中的每一个触发一个单独的触发器。因此,这样的并置计划可能会重叠,并行或立即连续执行多次。请确保您指定的 cron 表达式等不会意外重叠。
@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.
从 Spring Framework 4.3 开始, As of Spring Framework 4.3, 确保在运行时不初始化同一 Make sure that you are not initializing multiple instances of the same |
The @Scheduled
annotation on Reactive methods or Kotlin suspending functions
从 Spring Framework 6.1 开始,@Scheduled
方法还受支持于多种类型的响应方法:
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 ofPublisher
) 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 theReactiveAdapterRegistry
, provided the type supports deferred subscription like in the following example:
@Scheduled(fixedDelay = 500)
public Single<String> rxjavaNonPublisher() {
return Single.just("example");
}
The |
-
Kotlin suspending functions, like in the following example:
@Scheduled(fixedDelay = 500)
suspend fun something() {
// do something asynchronous
}
-
methods that return a Kotlin
Flow
orDeferred
instance, like in the following example:
@Scheduled(fixedDelay = 500)
fun something(): Flow<Void> {
flow {
// do something asynchronous
}
}
所有这些类型的办法都必须在没有参数的情况下宣告。在 Kotlinsuspending 函数的情况下,kotlinx.coroutines.reactor
桥接器也必须存在,以允许框架调用一个 suspending 函数作为 Publisher
。
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
.
Spring 框架将为带注释的方法获取一个 Publisher
一次,并将会安排一个 Runnable
,它将在其中订阅所述 Publisher
。这些内部的规则订阅根据相应的 cron
/fixedDelay
/fixedRate
配置发生。
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.
如果 Publisher
发射一个或多个 onNext
信号,它们将被忽略并丢弃(以与同步 @Scheduled
方法的返回值被忽略相同的方式)。
If the Publisher
emits onNext
signal(s), these are ignored and discarded (the same way
return values from synchronous @Scheduled
methods are ignored).
在下例中,Flux
每 5 秒发出 onNext("Hello")
、onNext("World")
,但是这些值没有被使用:
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");
}
如果 Publisher
发射一个 onError
信号,它将在 WARN
级别被记录并恢复。由于 Publisher
实例的异步和惰性特性,异常不会从 Runnable
任务中被抛出:这意味着 ErrorHandler
合同不会涉及响应式方法。
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.
在下例中,Mono
订阅在前 5 秒内失败了 2 次。然后订阅开始生效,每 5 秒向标准输出打印一条消息:
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"));
})
}
当销毁带注释的 bean 或关闭应用程序上下文时,Spring 框架会取消计划任务,其中包括下一个计划的 When destroying the annotated bean or closing the application context, Spring Framework cancels
scheduled tasks, which includes the next scheduled subscription to the |
The @Async
annotation
您可以在方法上提供 @Async
注释,以便异步调用该方法。换句话说,在调用时,调用者会立即返回,而方法的实际执行发生在已提交到 Spring TaskExecutor
的任务中。在最简单的情况下,您可以将此注释应用到返回 void
的方法,如下例所示:
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
}
与使用 @Scheduled
注释注释的方法不同,这些方法可以期待参数,因为它们是由调用者在运行时以“正常”的方式调用的,而不是由容器管理的计划任务调用的。例如,以下代码是 @Async
注释的合法应用:
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
}
即使是返回一个值的方法也可以异步调用。但是,这样的方法必须具有 Future
类型的返回值。这仍然提供了异步执行的好处,以便调用者可以在调用 Future
上的 get()
之前执行其他任务。以下示例展示了如何在返回一个值的方法上使用 @Async
:
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
与生命周期回调结合使用,例如 @PostConstruct
。要异步初始化 Spring bean,您当前必须使用一个单独的初始化 Spring bean,然后在目标上调用 @Async
注释的方法,如下例所示:
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 |
Executor Qualification with @Async
默认情况下,当在方法上指定 @Async
时,所使用的执行器是 configured when enabling async support, 即 “annotation-driven” 元素(如果你使用的是 XML)或你的 AsyncConfigurer
实现(如果有)。然而,当你需要表明在执行给定方法时需要使用一个不同于默认执行器的执行器时,可以使用 @Async
注解的 value
属性。以下示例展示了如何这样做:
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"
}
在这种情况下,“otherExecutor
”可以是 Spring 容器中任何 Executor
bean 的名称,或者它可以是与任何 Executor
关联的限定符的名称(例如,使用 <qualifier>
元素或 Spring 的 @Qualifier
注释指定)。
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
当一个 @Async
方法具有 Future
类型的返回值时,很容易管理在方法执行期间抛出的异常,因为在调用 Future
结果上的 get
时会抛出此异常。但是,对于 void
返回类型,异常不会被捕获且无法被传输。您可以提供一个 AsyncUncaughtExceptionHandler
来处理此类异常。以下示例展示了如何做到这一点:
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
}
}
默认情况下,异常只是被记录。您可以使用 AsyncConfigurer
或 <task:annotation-driven/>
XML 元素定义一个自定义 AsyncUncaughtExceptionHandler
。
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
从版本 3.0 开始,Spring 包含了一个 XML 命名空间,用于配置 TaskExecutor
和 TaskScheduler
实例。它还提供了一种方便的方法来配置要使用触发器调度的任务。
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
以下元素使用指定的线程池大小创建 ThreadPoolTaskScheduler
实例:
The following element creates a ThreadPoolTaskScheduler
instance with the
specified thread pool size:
<task:scheduler id="scheduler" pool-size="10"/>
为 id
属性提供的值被用作池中线程名称的前缀。scheduler
元素相对简单。如果您没有提供 pool-size
属性,则默认线程池只有一个线程。调度器没有其他配置选项。
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
以下内容创建一个 ThreadPoolTaskExecutor
实例:
The following creates a ThreadPoolTaskExecutor
instance:
<task:executor id="executor" pool-size="10"/>
与 previous section 中所示的计划程序一样,为 id
属性提供的值被用作池内线程名称的前缀。就池大小而言,executor
元素支持比 scheduler
元素更多的配置选项。首先,对于 ThreadPoolTaskExecutor
的线程池本身更具可配置性。执行器的线程池不仅仅是单个大小,还可以为核心大小和最大大小设置不同的值。如果你提供一个单一值,则执行器具有固定大小的线程池(核心和最大大小相同)。然而,executor
元素的 pool-size
属性也接受范围的形式 min-max
。以下示例设置了一个最小值 5
和一个最大值 25
:
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"/>
在之前的配置中,还提供了 queue-capacity
值。线程池的配置还应根据执行器的队列容量进行考虑。有关池大小和队列容量之间关系的完整说明,请参阅 ThreadPoolExecutor
的文档 https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/ThreadPoolExecutor.html。主要思想是,当提交一项任务时,如果活动线程数当前小于核心大小,执行器首先尝试使用一个空闲线程。如果已经达到核心大小,则会将该任务添加到队列中,只要其容量尚未达到上限。仅在队列的容量达到上限时,执行器才会创建一个超过核心大小的新线程。如果也达到最大值,则执行器会拒绝该任务。
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.
默认情况下,队列是无界的,但这很少是期望的配置,因为如果所有池线程都处于繁忙状态时,向该队列添加足够的任务可能会导致 OutOfMemoryErrors
。此外,如果队列是无界的,则最大值没有任何效果。由于执行器在创建超过核心大小的新线程之前总是先尝试队列,因此队列必须具有有限的容量,以便线程池增长超过核心大小(这就是固定大小池在使用无界队列时是唯一合理的场景的原因)。
By default, the queue is unbounded, but this is rarely the desired configuration,
because it can lead to OutOfMemoryErrors
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).
考虑上述所提到的场景,即当一个任务被拒绝时。默认情况下,当一个任务被拒绝时,线程池执行器会抛出一个 TaskRejectedException
。然而,拒绝策略实际上是可配置的。当使用默认拒绝策略(即 AbortPolicy
实现)时,会抛出异常。对于重负荷下可以跳过某些任务的应用程序,你可以配置 DiscardPolicy
或 DiscardOldestPolicy
。另一种适用于需要在重负载下限制已提交的任务的应用程序的选项是 CallerRunsPolicy
。该策略不会抛出异常或丢弃任务,而是强制调用提交方法的线程运行任务本身。其思想是,此类调用方在运行该任务时会处于繁忙状态,并且无法立即提交其他任务。因此,它提供了限制传入负载(同时保持线程池和队列的限制)的简单方法。通常情况下,这允许执行器“赶上”它正在处理的任务,从而释放队列、池或两者的部分容量。你可以从 executor
元素上 rejection-policy
属性的枚举值中选择任何这些选项。
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.
以下示例显示了一个 executor
元素,该元素具有多个属性以指定各种行为:
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"/>
最后,keep-alive
设置确定线程在停止之前可以保持空闲状态的时间限制(以秒为单位)。如果池中当前的线程数量超过核心数量,则在等待此段时间且没有处理任务后,将停止多余的线程。零值会造成多余的线程在执行任务且任务队列中没有后续工作后立即停止。以下示例将 keep-alive
值设置为两分钟:
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
Spring 任务命名空间最强大的功能是对在 Spring Application Context 中计划的任务进行配置的支持。这种方式与 Spring 中其他“方法调用者”类似,例如 JMS 命名空间为配置消息驱动的 POJO 提供的方法调用者。基本上,ref
属性可以指向任何 Spring 管理的对象,而 method
属性提供要在该对象上调用的方法的名称。以下列表显示了一个简单的示例:
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"/>
调度程序由外部元素引用,每个单独的任务都包含其触发器元数据配置。在前一个示例中,元数据定义了一个周期性触发器,其中固定延迟指定在每个任务执行完成后等待的毫秒数。另一种选择是 fixed-rate
, 它指示该方法应运行的频率,而不考虑任何先前执行需要多长时间。此外,对于 fixed-delay
和 fixed-rate
任务,你可以指定一个“初始延迟”参数,它指示在方法首次执行之前要等待的毫秒数。为了更好地控制,你可以提供一个 cron
属性来提供一个 cron expression。以下示例展示了这些其他选项:
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
无论是在 @Scheduled
annotations,task:scheduled-tasks
elements, 还是在其他地方使用,所有的 Spring cron 表达式都必须符合相同的格式。有效的 cron 表达式(如 * * * * * *
)包含六个以空格分隔的时间和日期字段,每个字段都有自己的有效值范围:
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
orDDDL
), it means the last day of week (d
orDDD
) in the month.
-
-
The day-of-month field can be
nW
, which stands for the nearest weekday to day of the monthn`
. If `n falls on Saturday, this yields the Friday before it. Ifn
falls on Sunday, this yields the Monday after, which also happens ifn
is1
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
(orDDD#n
), which stands for then`th day of week `d
(orDDD
) in the month.
这里一些示例:
Here are some examples:
Cron Expression | Meaning |
---|---|
|
top of every hour of every day |
|
every ten seconds |
|
8, 9 and 10 o’clock of every day |
|
6:00 AM and 7:00 PM every day |
|
8:00, 8:30, 9:00, 9:30, 10:00 and 10:30 every day |
|
on the hour nine-to-five weekdays |
|
every Christmas Day at midnight |
|
last day of the month at midnight |
|
third-to-last day of the month at midnight |
|
last Friday of the month at midnight |
|
last Thursday of the month at midnight |
|
first weekday of the month at midnight |
|
last weekday of the month at midnight |
|
the second Friday in the month at midnight |
|
the first Monday in the month at midnight |
Macros
诸如 0 0 * * * *
的表达式对人类来说很难解析,因此在出现错误的情况下很难修复。为了提高可读性,Spring 支持以下宏,表示常用的序列。你可以使用这些宏代替六位数字的值,如下所示:@Scheduled(cron = "@hourly")
。
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 |
---|---|
|
once a year ( |
|
once a month ( |
|
once a week ( |
|
once a day ( |
|
once an hour, ( |
Using the Quartz Scheduler
Quartz 使用 Trigger
, Job
, 和 JobDetail
对象来实现各种作业的调度。有关 Quartz 背后的基本概念,请参阅 Quartz Web site。为了方便,Spring 提供了几种类,简化了在基于 Spring 的应用程序中使用 Quartz。
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
对象包含运行作业所需的所有信息。Spring 提供了一个 JobDetailFactoryBean
,它为 XML 配置目的提供了 bean 样式的属性。考虑以下示例:
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>
作业详细配置具有运行作业(ExampleJob
)所需的所有信息。超时在作业数据映射中指定。作业数据映射可通过 JobExecutionContext
(在执行时传递给你)获得,但 JobDetail
也从映射到作业实例属性的作业数据获取其属性。因此,在以下示例中,ExampleJob
包含一个名为 timeout
的 bean 属性,JobDetail
会自动应用该属性:
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:
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 |
Using the MethodInvokingJobDetailFactoryBean
通常,你只需要在特定对象上调用一个方法。通过使用 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>
上一个示例导致在 exampleBusinessObject
方法上调用 doIt
方法,如下一个示例所示:
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"/>
通过使用 MethodInvokingJobDetailFactoryBean
,无需创建仅调用方法的单行作业。只需创建实际业务对象并连接详细信息对象即可。
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.
默认情况下,Quartz 作业是无状态的,这会导致作业相互干扰的可能性。如果针对同一个 JobDetail
指定两个触发器,第二个触发器有可能在第一个作业完成之前启动。如果 JobDetail
类实现了 Stateful
接口,这种情况不会发生:第二个作业不会在第一个作业完成之前启动。
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.
若要使 MethodInvokingJobDetailFactoryBean
产生的作业变为非并发,请将 concurrent
标志设置为 false
,如下例所示:
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
我们已经创建了作业详细信息和作业。我们还查看了让你在一个特定对象中调用方法的便捷 Bean。当然,我们仍然需要安排作业本身。这是通过使用触发器和 SchedulerFactoryBean
来完成的。在 Quartz 中有几种触发器可用,而 Spring 提供了两个 Quartz FactoryBean
实现,带有便捷的默认值:CronTriggerFactoryBean
和 SimpleTriggerFactoryBean
。
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
.
需要安排触发器。Spring 提供了一个 SchedulerFactoryBean
,用于将触发器公开为属性设置。SchedulerFactoryBean
使用这些触发器安排实际作业。
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.
以下清单同时使用了 SimpleTriggerFactoryBean
和 CronTriggerFactoryBean
:
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>
前一个示例设置了两个触发器,一个每 50 秒运行一次,启动延迟 10 秒,另一个每天上午 6 点运行一次。为最终确定一切,我们需要设置 SchedulerFactoryBean
,如下例所示:
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>
SchedulerFactoryBean
有更多属性可用,例如任务详细信息使用的日历、用于自定义 Quartz 的属性以及 Spring 提供的 JDBC DataSource。请参见 SchedulerFactoryBean
javadoc 以获取更多信息。
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.
|
|