对于最新的稳定版本,请使用 Spring Framework 6.2.0! |
任务执行和调度
Spring Framework 为
任务。Spring 也
具有支持线程池或委托给
CommonJ 中的请求。最终,这些
公共接口背后的实现抽象出
Java SE 和 Jakarta EE 环境。TaskExecutor
TaskScheduler
Spring 还具有集成类,以支持使用 Quartz Scheduler 进行调度。
Spring 抽象TaskExecutor
Executors 是线程池概念的 JDK 名称。“executor” 命名是 由于无法保证底层实现是 实际上是一个游泳池。执行程序可以是单线程的,甚至可以是同步的。Spring的 abstraction 隐藏了 Java SE 和 Jakarta EE 环境之间的实现细节。
Spring 的接口与接口相同。事实上,最初它存在的主要原因是抽象出来
使用线程池时需要 Java 5。该接口只有一个方法
() 接受基于语义的任务执行
以及线程池的配置。TaskExecutor
java.util.concurrent.Executor
execute(Runnable task)
最初创建它是为了给其他 Spring 组件一个抽象
用于需要的线程池。组件(如 、
JMS 和 Quartz 集成都使用抽象来池化线程。但是,如果您的 bean 需要线程池
行为,您也可以根据自己的需要使用此抽象。TaskExecutor
ApplicationEventMulticaster
AbstractMessageListenerContainer
TaskExecutor
TaskExecutor
类型
Spring 包含许多预构建的 .
您很可能永远不需要实现自己的。
Spring 提供的变体如下:TaskExecutor
-
SyncTaskExecutor
: 此实现不会异步运行调用。相反,每个 调用发生在调用线程中。它主要用于各种情况 不需要多线程处理,例如在简单的测试用例中。 -
SimpleAsyncTaskExecutor
: 此实现不会重用任何线程。相反,它会启动一个新线程 对于每个调用。但是,它确实支持阻止 在释放槽之前超过限制的任何调用。如果你 正在寻找真正的池化,请参阅此列表后面的 。ThreadPoolTaskExecutor
-
ConcurrentTaskExecutor
: 此实现是实例的适配器。 还有另一种 () 将配置参数公开为 Bean 属性。很少需要直接使用。但是,如果不是 足够灵活,满足您的需求,是一种选择。java.util.concurrent.Executor
ThreadPoolTaskExecutor
Executor
ConcurrentTaskExecutor
ThreadPoolTaskExecutor
ConcurrentTaskExecutor
-
ThreadPoolTaskExecutor
: 此实现是最常用的。它公开了用于配置的 bean 属性 a 并将其包装在 . 如果你需要适应不同类型的 , 我们建议您改用 A。java.util.concurrent.ThreadPoolExecutor
TaskExecutor
java.util.concurrent.Executor
ConcurrentTaskExecutor
-
DefaultManagedTaskExecutor
: 此实现使用在 JSR-236 中获得的 JNDI 兼容的运行时环境(例如 Jakarta EE 应用程序服务器), 为此,替换 CommonJ WorkManager。ManagedExecutorService
从 6.1 开始,提供暂停/恢复功能和优雅的
shutdown 的 Spring 生命周期管理。还有一个新的 “virtualThreads”
选项与 JDK 21 的虚拟线程保持一致,
以及 的正常关机功能。ThreadPoolTaskExecutor
SimpleAsyncTaskExecutor
SimpleAsyncTaskExecutor
使用TaskExecutor
Spring 的实现通常与依赖项注入一起使用。
在下面的示例中,我们定义了一个 bean,它使用 异步打印出一组消息:TaskExecutor
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));
}
}
}
如您所见,而不是从池中检索线程并自己执行它,
您将 your 添加到队列中。然后 使用其内部规则
确定任务的运行时间。Runnable
TaskExecutor
为了配置使用的规则,我们公开了简单的 bean 属性: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>
Spring 抽象TaskScheduler
除了抽象之外, Spring 还有一个带有
将任务安排在将来某个时间点运行的各种方法。以下内容
清单显示了接口定义:TaskExecutor
TaskScheduler
TaskScheduler
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);
最简单的方法是 named 方法,它只接受 a 和 an 。
这会导致任务在指定时间后运行一次。所有其他方法
能够调度任务以重复运行。固定速率和固定延迟
methods 用于简单的定期执行,但接受 a
更灵活。schedule
Runnable
Instant
Trigger
Trigger
接口
该接口本质上受到 JSR-236 的启发。的基本思想是,执行时间可以根据过去的执行结果确定,或者
甚至是任意的条件。如果这些决定考虑到
之前,该信息在 .接口本身非常简单,如下面的清单所示:Trigger
Trigger
TriggerContext
Trigger
public interface Trigger {
Instant nextExecution(TriggerContext triggerContext);
}
这是最重要的部分。它封装了所有
相关数据,并在必要时开放扩展。这是一个接口(实现由
default) 的 S S以下清单显示了可用于实现的方法。TriggerContext
TriggerContext
SimpleTriggerContext
Trigger
public interface TriggerContext {
Clock getClock();
Instant lastScheduledExecution();
Instant lastActualExecution();
Instant lastCompletion();
}
Trigger
实现
Spring 提供了该接口的两种实现。最有趣的一个
是 .它支持基于 cron 表达式调度任务。
例如,以下任务计划在每小时后 15 分钟运行,但仅运行
在工作日朝九晚五的“营业时间”内:Trigger
CronTrigger
scheduler.schedule(task, new CronTrigger("0 15 9-17 * * MON-FRI"));
另一个实现是接受固定的
period、可选的 initial delay 值和一个布尔值,用于指示 period
应解释为 fixed-rate 或 fixed-delay。由于该接口已经定义了以固定速率调度任务的方法,或者使用
fixed delay,则应尽可能直接使用这些方法。实现的价值在于,您可以在依赖
抽象。例如,允许周期性触发器可能很方便,
基于 cron 的触发器,甚至是可以互换使用的自定义触发器实现。
这样的组件可以利用依赖关系注入,以便您可以配置
这样,因此很容易修改或扩展它们。PeriodicTrigger
TaskScheduler
PeriodicTrigger
Trigger
Triggers
TaskScheduler
实现
与 Spring 的抽象一样,这种安排的主要好处是应用程序的调度需求与部署解耦
环境。在部署到
应用程序服务器环境中,线程不应由
应用程序本身。对于此类场景, Spring 提供了一个委托给 Jakarta EE 环境中的 JSR-236 的版本。TaskExecutor
TaskScheduler
DefaultManagedTaskScheduler
ManagedScheduledExecutorService
每当不需要外部线程管理时,更简单的替代方案是
应用程序内的本地设置,可进行调整
通过 Spring 的 .为方便起见, Spring 还提供了一个 ,它在内部委托给 a 以提供常见的 bean 样式配置,如下所示。
这些变体非常适合 lenient 中的本地嵌入式线程池设置
应用程序服务器环境,尤其是在 Tomcat 和 Jetty 上。ScheduledExecutorService
ConcurrentTaskScheduler
ThreadPoolTaskScheduler
ScheduledExecutorService
ThreadPoolTaskExecutor
从 6.1 开始,提供暂停/恢复功能和优雅的
shutdown 的 Spring 生命周期管理。还有一个名为 which new option for which set set with JDK 21 的 Virtual Threads,它使用
单个计划程序线程,但为每个计划任务执行触发一个新线程
(除了固定延迟任务,它们都在单个调度程序线程上运行,因此对于
这个虚拟线程对齐的选项,建议使用 Fixed Rates 和 cron 触发器)。ThreadPoolTaskScheduler
SimpleAsyncTaskScheduler
对调度和异步执行的注释支持
Spring 为任务调度和异步方法都提供了 Comments 支持 执行。
启用计划注释
要启用对 and 注释的支持,您可以将 and 添加到其中一个类中,如下例所示:@Scheduled
@Async
@EnableScheduling
@EnableAsync
@Configuration
@Configuration
@EnableAsync
@EnableScheduling
public class AppConfig {
}
您可以为您的应用程序选择相关的注释。例如
如果只需要支持 ,则可以省略 。了解更多
fine-grained control 中,您还可以实现 interface 和/或 interface。有关完整详细信息,请参见 SchedulingConfigurer
和 AsyncConfigurer
javadoc。@Scheduled
@EnableAsync
SchedulingConfigurer
AsyncConfigurer
如果您更喜欢 XML 配置,可以使用元素
如下例所示:<task:annotation-driven>
<task:annotation-driven executor="myExecutor" scheduler="myScheduler"/>
<task:executor id="myExecutor" pool-size="5"/>
<task:scheduler id="myScheduler" pool-size="10"/>
请注意,对于前面的 XML,提供了一个执行程序引用来处理这些
与带有注解的方法对应的任务,以及 scheduler
提供了管理那些带有 .@Async
@Scheduled
处理 annotation 的默认通知模式是 which allows
仅用于通过代理拦截呼叫。同一类中的本地呼叫
不能以这种方式被拦截。对于更高级的拦截模式,请考虑
切换到与编译时或加载时编织相结合的模式。@Async proxy aspectj |
注释@Scheduled
您可以将注释与触发器元数据一起添加到方法中。为
示例,以下方法每 5 秒(5000 毫秒)调用一次,其中
固定延迟,这意味着该时间段从每个
之前调用。@Scheduled
@Scheduled(fixedDelay = 5000)
public void doSomething() {
// something that should run periodically
}
默认情况下,毫秒将用作固定延迟、固定速率和
初始延迟值。如果您想使用不同的时间单位,例如秒或
分钟,您可以通过 中的属性进行配置。 例如,前面的示例也可以写成如下。
|
如果您需要固定速率执行,可以使用
注解。以下方法每 5 秒调用一次(在
每个调用的连续开始时间):fixedRate
@Scheduled(fixedRate = 5, timeUnit = TimeUnit.SECONDS)
public void doSomething() {
// something that should run periodically
}
对于固定延迟和固定速率任务,您可以通过指示
首次执行该方法之前要等待的时间,如下例所示:fixedRate
@Scheduled(initialDelay = 1000, fixedRate = 5000)
public void doSomething() {
// something that should run periodically
}
对于一次性任务,您只需通过指示 amount 来指定初始延迟 在预期执行方法之前等待的时间:
@Scheduled(initialDelay = 1000)
public void doSomething() {
// something that should run only once
}
如果简单的定期调度不够表达,你可以提供一个 cron 表达式。 以下示例仅在工作日运行:
@Scheduled(cron="*/5 * * * * MON-FRI")
public void doSomething() {
// something that should run on weekdays only
}
您还可以使用该属性指定 cron
表达式已解决。zone |
请注意,要调度的方法必须具有 void 返回值,并且不能接受任何 参数。如果该方法需要与应用程序中的其他对象交互 context 中,这些通常是通过依赖项注入提供的。
@Scheduled
可用作可重复的注释。如果多个计划声明
在相同的方法中找到,则每个 API 都将独立处理,并带有
他们每个人的单独扳机触发。因此,此类共址时间表
可以重叠并并行或立即连续执行多次。
请确保您指定的 cron 表达式等不会意外重叠。
从 Spring Framework 4.3 开始,任何范围的 bean 都支持方法。 确保您没有在运行时初始化同一 Comments 类的多个实例,除非您确实希望为每个此类
实例。与此相关,请确保您不要在 bean 上使用
带有 Comments 并注册为常规 Spring bean 的类
与容器一起。否则,您将获得双重初始化(一次通过
container 和 once through the aspect),结果是每个方法被调用两次。 |
Reactive 方法或 Kotlin 挂起函数的注解@Scheduled
从 Spring Framework 6.1 开始,多种类型也支持方法
响应式方法:@Scheduled
-
具有返回类型(或任何具体实现)的方法 如以下示例所示:
Publisher
Publisher
@Scheduled(fixedDelay = 500)
public Publisher<Void> reactiveSomething() {
// return an instance of Publisher
}
-
返回类型的方法,可通过共享实例进行调整 中,前提是该类型支持延迟订阅,例如 在以下示例中:
Publisher
ReactiveAdapterRegistry
@Scheduled(fixedDelay = 500)
public Single<String> rxjavaNonPublisher() {
return Single.just("example");
}
该类是通常可以调整的类型示例
更改为,但不支持延迟订阅。它在
registry 表示通过让方法返回 . |
-
Kotlin 挂起函数,如以下示例所示:
@Scheduled(fixedDelay = 500)
suspend fun something() {
// do something asynchronous
}
-
返回 Kotlin 或实例的方法,如以下示例所示:
Flow
Deferred
@Scheduled(fixedDelay = 500)
fun something(): Flow<Void> {
flow {
// do something asynchronous
}
}
所有这些类型的方法都必须在声明时不带任何参数。以 Kotlin 为例
suspending 函数,则 bridge 也必须存在以允许
用于调用挂起函数的框架。kotlinx.coroutines.reactor
Publisher
Spring 框架将获取一次 for an-comments 方法,并将
安排 A 订阅所述 .这些内部规则
订阅根据相应的 // 配置进行。Publisher
Runnable
Publisher
cron
fixedDelay
fixedRate
如果 发出信号,则忽略并丢弃这些信号(以相同的方式
来自同步方法的返回值将被忽略)。Publisher
onNext
@Scheduled
在以下示例中,每 5 次发出
秒,但这些值未使用:Flux
onNext("Hello")
onNext("World")
@Scheduled(initialDelay = 5000, fixedRate = 5000)
public Flux<String> reactiveSomething() {
return Flux.just("Hello", "World");
}
如果 发出信号,则将其记录在 level 并恢复。
由于实例的异步和惰性,异常是
not throwing from the task:这意味着 Contract 不是
涉及响应式方法。Publisher
onError
WARN
Publisher
Runnable
ErrorHandler
因此,尽管出现错误,但仍会进行进一步的计划订阅。
在以下示例中,订阅在前 5 秒内失败两次。
然后订阅开始成功,每 5 次将消息打印到标准输出
秒:Mono
@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 框架会取消
计划任务,其中包括
与当前仍处于活动状态的任何过去订阅一样(例如,对于长时间运行的发布者
甚至无限的出版商)。 |
注释@Async
您可以在方法上提供注释,以便调用该方法
异步发生。换句话说,调用方在
调用,而该方法的实际执行发生在已
提交到 Spring 。在最简单的情况下,您可以应用注释
转换为返回 的方法,如下例所示:@Async
TaskExecutor
void
@Async
void doSomething() {
// this will be run asynchronously
}
与使用 annotation 注释的方法不同,这些方法可以预期
参数,因为它们是由调用方在运行时以“正常”方式调用的,而不是
而不是容器管理的计划任务。例如,以下
code 是 annotation 的合法应用程序:@Scheduled
@Async
@Async
void doSomething(String s) {
// this will be run asynchronously
}
即使是返回值的方法也可以异步调用。但是,此类方法
都需要具有 -typed 返回值。这仍然提供了以下好处:
异步执行,以便调用方可以在调用该 .以下示例演示如何对方法
返回一个值:Future
get()
Future
@Async
@Async
Future<String> returnSomething(int i) {
// this will be run asynchronously
}
@Async 方法不仅可以声明常规的返回
type 和 Spring 的 or 一样,截至
Spring 4.2 是 JDK 8 的 ,用于更丰富的交互
异步任务,并立即组合与进一步的处理步骤。java.util.concurrent.Future org.springframework.util.concurrent.ListenableFuture java.util.concurrent.CompletableFuture |
您不能与生命周期回调结合使用,例如 .
要异步初始化 Spring bean,您当前必须使用单独的
初始化 Spring Bean,然后在目标上调用带注释的方法,
如下例所示:@Async
@PostConstruct
@Async
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();
}
}
没有直接的 XML 等效项,因为应该设计这样的方法
首先,对于异步执行,而不是在外部重新声明为 asynchronous。
但是,您可以使用 Spring AOP 手动设置 Spring,
与自定义切入点结合使用。@Async AsyncExecutionInterceptor |
遗嘱执行人资格@Async
默认情况下,在方法上指定时,使用的 Executor 是
一个在启用异步支持时配置,
即 “annotation-driven” 元素(如果您使用的是 XML 或您的实现)(如果有)。但是,当您需要指示 default 以外的执行程序应为
在执行给定方法时使用。以下示例显示了如何执行此操作:@Async
AsyncConfigurer
value
@Async
@Async("otherExecutor")
void doSomething(String s) {
// this will be run asynchronously by "otherExecutor"
}
在这种情况下, 可以是 Spring 中任何 bean 的名称
container,也可以是与任何 (例如,
使用元素或 Spring 的注释指定)。"otherExecutor"
Executor
Executor
<qualifier>
@Qualifier
异常管理@Async
当方法具有 -typed 返回值时,它很容易管理
在方法执行期间引发的异常,因为此异常是
调用 result 时抛出。使用 return 类型时,
但是,异常未捕获,无法传输。您可以提供 an 来处理此类异常。以下示例显示了
如何操作:@Async
Future
get
Future
void
AsyncUncaughtExceptionHandler
public class MyAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {
@Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
// handle exception
}
}
默认情况下,仅记录异常。您可以使用 或 XML 元素定义自定义。AsyncUncaughtExceptionHandler
AsyncConfigurer
<task:annotation-driven/>
命名空间task
从版本 3.0 开始, Spring 包括一个用于配置和实例的 XML 名称空间。它还提供了一种将任务配置为
使用触发器进行调度。TaskExecutor
TaskScheduler
'scheduler' 元素
以下元素创建一个实例,其中
指定的线程池大小:ThreadPoolTaskScheduler
<task:scheduler id="scheduler" pool-size="10"/>
为该属性提供的值用作线程名称的前缀
在池中。该元素相对简单。如果你没有
提供属性,则默认线程池只有一个线程。
调度程序没有其他配置选项。id
scheduler
pool-size
元素executor
下面将创建一个实例:ThreadPoolTaskExecutor
<task:executor id="executor" pool-size="10"/>
与上一节中所示的调度程序一样,
为该属性提供的值用作
游泳池。就池大小而言,该元素支持更多
配置选项。首先,用于
a 本身的可配置性更强。不仅仅是单一尺寸,
执行程序的线程池的 Core 和 Max Size 可以具有不同的值。
如果提供单个值,则执行程序具有固定大小的线程池(核心和
最大大小相同)。但是,该元素的属性也
接受 形式的范围 。以下示例将最小值设置为最大值 :id
executor
scheduler
ThreadPoolTaskExecutor
executor
pool-size
min-max
5
25
<task:executor
id="executorWithPoolSizeRange"
pool-size="5-25"
queue-capacity="100"/>
在前面的配置中,还提供了一个值。
线程池的配置也应该根据
执行程序的队列容量。有关 pool 之间关系的完整描述
大小和队列容量,请参阅 ThreadPoolExecutor
的文档。
主要思想是,当提交任务时,执行程序首先尝试使用
Free Thread (如果当前活动线程数小于核心大小)。
如果已达到核心大小,则任务将添加到队列中,只要其
尚未达到容量。只有这样,如果队列的容量已
reached,则 Executor 是否会创建一个超出 Core 大小的新线程。如果最大大小
,则执行程序拒绝该任务。queue-capacity
默认情况下,队列是无界的,但这很少是所需的配置。
因为它可能导致如果向该队列添加了足够的任务,而
所有池线程都处于繁忙状态。此外,如果队列是无界的,则最大大小为
完全没有效果。由于 executor 总是在创建新的
线程超出核心大小,则队列必须具有线程池的有限容量
增长到超过核心大小(这就是为什么固定大小的池是唯一明智的情况
当使用无界队列时)。OutOfMemoryError
如上所述,考虑任务被拒绝的情况。默认情况下,当
task 被拒绝时,线程池执行程序会抛出一个 .然而
拒绝策略实际上是可配置的。使用
默认拒绝策略,即 implementation。
对于在重负载下可以跳过某些任务的应用程序,您可以改为
配置 或 。另一个有效的选择
对于需要在重负载下限制提交任务的应用程序来说,是
这。不是抛出异常或丢弃任务,
该策略强制调用 submit 方法的线程运行任务本身。
这个想法是这样的调用者在运行该任务时很忙,无法提交
其他任务。因此,它提供了一种简单的方法来限制传入的
load 同时保持线程池和队列的限制。通常,这允许
执行程序 “赶上” 它正在处理的任务,从而释放一些
容量。您可以从
元素上属性的可用值的枚举。TaskRejectedException
AbortPolicy
DiscardPolicy
DiscardOldestPolicy
CallerRunsPolicy
rejection-policy
executor
以下示例显示了一个元素,其中包含许多要指定的属性
各种行为:executor
<task:executor
id="executorWithCallerRunsPolicy"
pool-size="5-25"
queue-capacity="100"
rejection-policy="CALLER_RUNS"/>
最后,该设置确定线程的时间限制(以秒为单位)
在停止之前可能会保持空闲状态。如果线程数超过核心数
当前在池中,在等待此时间而不处理任务后,超出
线程停止。时间值为零会导致过多的线程停止
在执行任务后立即执行,而不在任务队列中保留后续工作。
以下示例将该值设置为 2 分钟:keep-alive
keep-alive
<task:executor
id="executorWithKeepAlive"
pool-size="5-25"
keep-alive="120"/>
'scheduled-tasks' 元素
Spring 的 task 命名空间最强大的功能是支持配置
要在 Spring Application Context 中调度的任务。这遵循一种方法
类似于 Spring 中的其他“方法调用程序”,例如 JMS 名称空间提供的
用于配置消息驱动的 POJO。基本上,属性可以指向任何
Spring 管理的对象,并且该属性提供了要
在该对象上调用。下面的清单显示了一个简单的示例:ref
method
<task:scheduled-tasks scheduler="myScheduler">
<task:scheduled ref="beanA" method="methodA" fixed-delay="5000"/>
</task:scheduled-tasks>
<task:scheduler id="myScheduler" pool-size="10"/>
调度程序由外部元素引用,每个单独的
task 包含其触发器元数据的配置。在前面的示例中,
该元数据定义了一个周期性触发器,该触发器具有固定的延迟,指示
毫秒,以便在每个任务执行完成后等待。另一个选项是 ,指示该方法应运行的频率,而不管多长时间
任何以前的执行都需要。此外,对于这两个任务,您可以指定一个 'initial-delay' 参数,指示
毫秒,以便在首次执行方法之前等待。如需更多控制,
您可以改为提供 attribute 来提供 cron 表达式。
以下示例显示了以下其他选项:fixed-rate
fixed-delay
fixed-rate
cron
<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 表达式
所有 Spring cron 表达式都必须遵循相同的格式,无论您是在 @Scheduled
注解、task:scheduled-tasks
元素中使用它们,还是将它们用于
或者其他地方。格式正确的 cron 表达式(如 )由 6 个
以空格分隔的 time 和 date 字段,每个字段都有自己的有效值范围:* * * * * *
┌───────────── 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) │ │ │ │ │ │ * * * * * *
有一些适用的规则:
-
字段可以是星号 (),它始终代表“first-last”。 对于日期或星期字段,可以使用问号 () 代替 星号。
*
?
-
逗号 () 用于分隔列表中的项目。
,
-
用连字符 () 分隔的两个数字表示一个数字范围。 指定的范围是非独占的。
-
-
跟在范围 (或 ) 后面指定数字值在该范围内的间隔。
*
/
-
英文名称也可用于 month 和 day-of-week 字段。 使用特定日期或月份的前三个字母(大小写无关紧要)。
-
day-of-month 和 day-of-week 字段可以包含具有不同含义的字符。
L
-
在 day-of-month 字段中,表示该月的最后一天。 如果后跟负偏移量(即 ),则表示该月的第
n
天到倒数第 n 天。L
L-n
-
在 day-of-week 字段中,表示一周的最后一天。 如果前缀为数字或三个字母的名称 ( 或 ),则表示该月中一周的最后一天 (
d
或DDD
)。L
dL
DDDL
-
-
day-of-month 字段可以是 ,它表示最接近
n
月的 day 的工作日。 如果落在星期六,则生成之前的星期五。 如果落在星期日,则生成 Monday after,如果 is 和 Falls on ,也会发生这种情况 星期六(即:代表当月的第一个工作日)。nW
n
n
n
1
1W
-
如果 day-of-month 字段为 ,则表示该月的最后一个工作日。
LW
-
day-of-week 字段可以是 (或 ),它代表该月第
d
周(或DDD
)的第n
天。d#n
DDD#n
以下是一些示例:
Cron 表达式 | 意义 |
---|---|
|
每天每小时的顶部 |
|
每十秒 |
|
每天 8 点、9 点和 10 点 |
|
每天上午 6:00 和晚上 7:00 |
|
每天 8:00、8:30、9:00、9:30、10:00 和 10:30 |
|
工作日朝九晚五 |
|
每个圣诞节的午夜 |
|
每月的最后一天午夜 |
|
每月倒数第三天的午夜 |
|
每月最后一个星期五午夜 |
|
每月最后一个星期四午夜 |
|
每月第一个工作日的午夜 |
|
每月最后一个工作日午夜 |
|
每月第二个星期五的午夜 |
|
每月第一个星期一的午夜 |
宏
诸如 such 之类的表达式对于人类来说很难解析,因此,
如果出现错误,很难修复。为了提高可读性,Spring 支持以下功能
宏,表示常用序列。您可以改用这些宏
的六位数值,因此: .0 0 * * * *
@Scheduled(cron = "@hourly")
宏 | 意义 |
---|---|
|
每年一次 ( |
|
每月一次 ( |
|
每周一次 ( |
|
每天一次 (),或 |
|
每小时一次,( |
使用 Quartz 调度程序
Quartz 使用 、 和 对象来实现所有
工作种类。有关 Quartz 背后的基本概念,请参阅 Quartz Web 站点。为方便起见,Spring
提供了几个类,这些类简化了在基于 Spring 的应用程序中使用 Quartz。Trigger
Job
JobDetail
使用JobDetailFactoryBean
Quartz 对象包含运行作业所需的所有信息。Spring
提供了一个 ,它为 XML 提供 Bean 样式的属性
配置目的。请考虑以下示例:JobDetail
JobDetailFactoryBean
<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>
作业详细信息配置包含运行作业 () 所需的所有信息。
超时在作业数据映射中指定。作业数据映射可通过 (在执行时传递给您) 获得,但 也会获取
其 properties 从 Job 数据映射到 Job 实例的 properties 中。因此,在
以下示例包含一个名为 的 bean 属性,并且 已自动应用它:ExampleJob
JobExecutionContext
JobDetail
ExampleJob
timeout
JobDetail
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
}
}
作业数据映射中的所有其他属性也可供您使用。
通过使用 and 属性,您可以修改名称和组
的工作。默认情况下,作业的名称与 Bean 名称匹配
的 ( 在上面的前面示例中)。name group JobDetailFactoryBean exampleJob |
使用MethodInvokingJobDetailFactoryBean
通常,您只需在特定对象上调用方法。通过使用 ,您可以完全执行此操作,如下例所示:MethodInvokingJobDetailFactoryBean
<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="exampleBusinessObject"/>
<property name="targetMethod" value="doIt"/>
</bean>
前面的示例导致在方法上调用方法,如下例所示:doIt
exampleBusinessObject
public class ExampleBusinessObject {
// properties and collaborators
public void doIt() {
// do the actual work
}
}
<bean id="exampleBusinessObject" class="examples.ExampleBusinessObject"/>
通过使用 ,您无需创建单行作业
,它仅调用一个方法。您只需创建实际的业务对象,并且
关联 Detail 对象。MethodInvokingJobDetailFactoryBean
默认情况下,Quartz 作业是无状态的,因此 Job 可能会干扰
彼此之间。如果为同一 指定两个触发器,则 是可能的
第二个作业在第一个作业完成之前启动。如果类
实现接口,则不会发生这种情况:第二个作业不会启动
在第一个完成之前。JobDetail
JobDetail
Stateful
要使 be non-concurrent 生成的作业,
将 flag 设置为 ,如下例所示:MethodInvokingJobDetailFactoryBean
concurrent
false
<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>
默认情况下,作业将以并发方式运行。 |
使用 Triggers 和 Wiring Up JobSchedulerFactoryBean
我们已经创建了 job details 和 jobs。我们还审查了便利 Bean
用于在特定对象上调用方法。当然,我们仍然需要将
工作本身。这是通过使用触发器和 .几个
触发器在 Quartz 中可用,并且 Spring 提供了两个具有方便默认值的 Quartz 实现:和 。SchedulerFactoryBean
FactoryBean
CronTriggerFactoryBean
SimpleTriggerFactoryBean
需要安排触发器。Spring 提供了一个公开
触发器设置为 Properties。 使用
那些触发器。SchedulerFactoryBean
SchedulerFactoryBean
下面的清单同时使用 a 和 a :SimpleTriggerFactoryBean
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
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="cronTrigger"/>
<ref bean="simpleTrigger"/>
</list>
</property>
</bean>
还有更多属性可用于 ,例如
作业详细信息、用于自定义 Quartz 的属性以及 Spring 提供的 JDBC DataSource。看
SchedulerFactoryBean
javadoc 了解更多信息。SchedulerFactoryBean
SchedulerFactoryBean 还可以识别 Classpath 中的文件,
基于 Quartz 属性键,与常规 Quartz 配置一样。请注意,许多设置与属性文件中的常见 Quartz 设置交互;
因此,建议不要在这两个级别都指定值。例如,不要设置
“org.quartz.jobStore.class”属性(如果您打算依赖 Spring 提供的 DataSource),
或指定一个 variant (变体),该
是标准 .quartz.properties SchedulerFactoryBean org.springframework.scheduling.quartz.LocalDataSourceJobStore org.quartz.impl.jdbcjobstore.JobStoreTX |