对于最新的稳定版本,请使用 Spring Framework 6.2.0! |
任务执行和调度
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。
使用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));
}
}
}
如您所见,而不是从池中检索线程并自己执行它,
您将Runnable
添加到队列中。然后TaskExecutor
使用其内部规则来
确定任务的运行时间。
要配置规则,TaskExecutor
uses,我们公开了简单的 bean 属性:
<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>
SpringTaskScheduler
抽象化
除了TaskExecutor
abstraction 的TaskScheduler
SPI 的
将任务安排在将来某个时间点运行的各种方法。以下内容
清单显示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);
最简单的方法是名为schedule
这只需要一个Runnable
以及一个Instant
.
这会导致任务在指定时间后运行一次。所有其他方法
能够调度任务以重复运行。固定速率和固定延迟
方法用于简单的定期执行,但接受Trigger
是
更灵活。
Trigger
接口
这Trigger
interface 本质上是受 JSR-236 的启发。的基本思想Trigger
是执行时间可以根据过去的执行结果确定,或者
甚至是任意的条件。如果这些决定考虑到
之前,该信息在TriggerContext
.这Trigger
Interface 本身非常简单,如下面的清单所示:
public interface Trigger {
Instant nextExecution(TriggerContext triggerContext);
}
这TriggerContext
是最重要的部分。它封装了所有
相关数据,并在必要时开放扩展。这TriggerContext
是一个接口(一个SimpleTriggerContext
implementation 由
default) 的 S S以下清单显示了Trigger
实现。
public interface TriggerContext {
Clock getClock();
Instant lastScheduledExecution();
Instant lastActualExecution();
Instant lastCompletion();
}
Trigger
实现
Spring 提供了Trigger
接口。最有趣的一个
是CronTrigger
.它支持基于 cron 表达式调度任务。
例如,以下任务计划在每小时后 15 分钟运行,但仅运行
在工作日朝九晚五的“营业时间”内:
scheduler.schedule(task, new CronTrigger("0 15 9-17 * * MON-FRI"));
另一个实现是PeriodicTrigger
接受固定的
period、可选的 initial delay 值和一个布尔值,用于指示 period
应解释为 fixed-rate 或 fixed-delay。由于TaskScheduler
接口已经定义了以固定速率调度任务的方法,或者
fixed delay,则应尽可能直接使用这些方法。的PeriodicTrigger
implementation 是你可以在依赖
这Trigger
抽象化。例如,允许周期性触发器可能很方便,
基于 cron 的触发器,甚至是可以互换使用的自定义触发器实现。
这样的组件可以利用依赖关系注入,以便您可以配置
这样Triggers
外部,因此可以轻松修改或扩展它们。
TaskScheduler
实现
与 Spring 的TaskExecutor
abstraction 的主要好处是TaskScheduler
安排是应用程序的调度需求与 Deployment 解耦
环境。在部署到
应用程序服务器环境中,线程不应由
应用程序本身。对于此类场景, Spring 提供了一个DefaultManagedTaskScheduler
委托给 JSR-236ManagedScheduledExecutorService
在 Jakarta EE 环境中。
每当不需要外部线程管理时,更简单的替代方案是
本地ScheduledExecutorService
在应用程序中进行设置,可进行调整
通过 Spring 的ConcurrentTaskScheduler
.为方便起见, Spring 还提供了一个ThreadPoolTaskScheduler
,它在内部委托给ScheduledExecutorService
提供常见的 bean 样式配置,如下所示ThreadPoolTaskExecutor
.
这些变体非常适合 lenient 中的本地嵌入式线程池设置
应用程序服务器环境,尤其是在 Tomcat 和 Jetty 上。
对调度和异步执行的注释支持
Spring 为任务调度和异步方法都提供了 Comments 支持 执行。
启用计划注释
要启用对@Scheduled
和@Async
annotations 中,您可以添加@EnableScheduling
和@EnableAsync
到你的@Configuration
类,如下例所示:
@Configuration
@EnableAsync
@EnableScheduling
public class AppConfig {
}
您可以为您的应用程序选择相关的注释。例如
如果您只需要支持@Scheduled
,您可以省略@EnableAsync
.了解更多
精细控制,您还可以实现SchedulingConfigurer
界面、AsyncConfigurer
接口,或两者兼而有之。请参阅SchedulingConfigurer
和AsyncConfigurer
javadoc 了解完整详细信息。
如果您更喜欢 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,提供了一个执行程序引用来处理这些
task 中,这些 task 对应于具有@Async
注解和调度程序
提供了 reference 来管理那些用@Scheduled
.
用于处理的默认通知模式@Async annotations 为proxy 这允许
仅用于通过代理拦截呼叫。同一类中的本地呼叫
不能以这种方式被拦截。对于更高级的拦截模式,请考虑
切换到aspectj 模式与编译时或加载时编织结合使用。 |
这@Scheduled
注解
您可以添加@Scheduled
Comments 以及 Trigger 元数据。为
示例,以下方法每 5 秒(5000 毫秒)调用一次,其中
固定延迟,这意味着该时间段从每个
之前调用。
@Scheduled(fixedDelay = 5000)
public void doSomething() {
// something that should run periodically
}
默认情况下,毫秒将用作固定延迟、固定速率和
初始延迟值。如果您想使用不同的时间单位,例如秒或
分钟,您可以通过 例如,前面的示例也可以写成如下。
|
如果您需要固定速率执行,可以使用fixedRate
属性中的
注解。以下方法每 5 秒调用一次(在
每个调用的连续开始时间):
@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
}
如果简单的定期调度不够表达,你可以提供一个 cron 表达式。 以下示例仅在工作日运行:
@Scheduled(cron="*/5 * * * * MON-FRI")
public void doSomething() {
// something that should run on weekdays only
}
您还可以使用zone 属性指定 cron
表达式已解决。 |
请注意,要调度的方法必须具有 void 返回值,并且不能接受任何 参数。如果该方法需要与应用程序中的其他对象交互 context 中,这些通常是通过依赖项注入提供的。
@Scheduled
可用作可重复的注释。如果多个计划声明
在相同的方法中找到,则每个 API 都将独立处理,并带有
他们每个人的单独扳机触发。因此,此类共址时间表
可以重叠并并行或立即连续执行多次。
请确保您指定的 cron 表达式等不会意外重叠。
从 Spring Framework 4.3 开始, 确保您没有初始化同一 |
这@Async
注解
您可以提供@Async
注解,以便调用该方法
异步发生。换句话说,调用方在
调用,而该方法的实际执行发生在已
提交到 SpringTaskExecutor
.在最简单的情况下,您可以应用注释
转换为返回void
,如下例所示:
@Async
void doSomething() {
// this will be run asynchronously
}
与用@Scheduled
注解,这些方法可以预期
参数,因为它们是由调用方在运行时以“正常”方式调用的,而不是
而不是容器管理的计划任务。例如,以下
code 是@Async
注解:
@Async
void doSomething(String s) {
// this will be run asynchronously
}
即使是返回值的方法也可以异步调用。但是,此类方法
都需要具有Future
-typed 返回值。这仍然提供了以下好处:
异步执行,以便调用方可以在调用get()
在那个Future
.以下示例演示如何使用@Async
在方法
返回一个值:
@Async
Future<String> returnSomething(int i) {
// this will be run asynchronously
}
@Async 方法不仅可以声明一个常规的java.util.concurrent.Future 返回
type 和 Spring 的org.springframework.util.concurrent.ListenableFuture 或者,截至
Spring 4.2 和 JDK 8 的java.util.concurrent.CompletableFuture ,以便与
异步任务,并立即组合与进一步的处理步骤。 |
您不能使用@Async
与生命周期回调结合使用,例如@PostConstruct
.
要异步初始化 Spring bean,您当前必须使用单独的
初始化 Spring Bean,然后调用@Async
annotated 方法、
如下例所示:
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 等效项@Async ,因为应该设计这样的方法
首先,对于异步执行,而不是在外部重新声明为 asynchronous。
但是,您可以手动设置 Spring 的AsyncExecutionInterceptor 使用 Spring AOP,
与自定义切入点结合使用。 |
遗嘱执行人资格@Async
默认情况下,在指定@Async
在方法中,使用的执行程序是
一个在启用异步支持时配置,
即 “annotation-driven” 元素(如果您使用的是 XML)或AsyncConfigurer
implementation (如果有)。但是,您可以使用value
属性的@Async
注解,当您需要指示除 default 以外的执行程序时,应为
在执行给定方法时使用。以下示例显示了如何执行此作:
@Async("otherExecutor")
void doSomething(String s) {
// this will be run asynchronously by "otherExecutor"
}
在这种情况下,"otherExecutor"
可以是任何Executor
Spring的 bean
container,也可以是与任何Executor
(例如,
如<qualifier>
元素或 Spring 的@Qualifier
注释)。
异常管理@Async
当@Async
method 具有Future
-type的返回值,易于管理
在方法执行期间引发的异常,因为此异常是
调用get
在Future
结果。使用void
return 类型,
但是,异常未捕获,无法传输。您可以提供AsyncUncaughtExceptionHandler
处理此类异常。以下示例显示了
如何作:
public class MyAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {
@Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
// handle exception
}
}
默认情况下,仅记录异常。您可以定义自定义AsyncUncaughtExceptionHandler
通过使用AsyncConfigurer
或<task:annotation-driven/>
XML 元素。
这task
Namespace
从版本 3.0 开始, Spring 包含一个 XML 名称空间,用于配置TaskExecutor
和TaskScheduler
实例。它还提供了一种将任务配置为
使用触发器进行调度。
'scheduler' 元素
以下元素创建一个ThreadPoolTaskScheduler
实例替换为
指定的线程池大小:
<task:scheduler id="scheduler" pool-size="10"/>
为id
attribute 用作线程名称的前缀
在池中。这scheduler
元素相对简单。如果你没有
提供pool-size
属性,则默认线程池只有一个线程。
调度程序没有其他配置选项。
这executor
元素
下面创建一个ThreadPoolTaskExecutor
实例:
<task:executor id="executor" pool-size="10"/>
与上一节中所示的调度程序一样,
为id
属性用作
游泳池。就池大小而言,executor
元素支持更多
配置选项比scheduler
元素。首先,用于
一个ThreadPoolTaskExecutor
本身的配置性更强。不仅仅是单一尺寸,
执行程序的线程池的 Core 和 Max Size 可以具有不同的值。
如果提供单个值,则执行程序具有固定大小的线程池(核心和
最大大小相同)。但是,executor
元素的pool-size
属性
接受min-max
.以下示例将最小值5
和最大值25
:
<task:executor
id="executorWithPoolSizeRange"
pool-size="5-25"
queue-capacity="100"/>
在前面的配置中,queue-capacity
value 也已提供。
线程池的配置也应该根据
执行程序的队列容量。有关 pool 之间关系的完整描述
size 和队列容量,请参阅ThreadPoolExecutor
.
主要思想是,当提交任务时,执行程序首先尝试使用
Free Thread (如果当前活动线程数小于核心大小)。
如果已达到核心大小,则任务将添加到队列中,只要其
尚未达到容量。只有这样,如果队列的容量已
reached,则 Executor 是否会创建一个超出 Core 大小的新线程。如果最大大小
,则执行程序拒绝该任务。
默认情况下,队列是无界的,但这很少是所需的配置。
因为它可能导致OutOfMemoryErrors
如果向该队列添加了足够的任务,而
所有池线程都处于繁忙状态。此外,如果队列是无界的,则最大大小具有
完全没有效果。由于 executor 总是在创建新的
线程超出核心大小,则队列必须具有线程池的有限容量
增长到超过核心大小(这就是为什么固定大小的池是唯一明智的情况
当使用无界队列时)。
如上所述,考虑任务被拒绝的情况。默认情况下,当
task 被拒绝时,线程池执行器会抛出一个TaskRejectedException
.然而
拒绝策略实际上是可配置的。使用
默认拒绝策略,即AbortPolicy
实现。
对于在重负载下可以跳过某些任务的应用程序,您可以改为
配置DiscardPolicy
或DiscardOldestPolicy
.另一个有效的选择
对于需要在重负载下限制提交任务的应用程序来说,是
这CallerRunsPolicy
.不是抛出异常或丢弃任务,
该策略强制调用 submit 方法的线程运行任务本身。
这个想法是这样的调用者在运行该任务时很忙,无法提交
其他任务。因此,它提供了一种简单的方法来限制传入的
load 同时保持线程池和队列的限制。通常,这允许
执行程序 “赶上” 它正在处理的任务,从而释放一些
容量。您可以从
可用于rejection-policy
属性executor
元素。
以下示例显示了executor
元素,其中包含许多要指定的属性
各种行为:
<task:executor
id="executorWithCallerRunsPolicy"
pool-size="5-25"
queue-capacity="100"
rejection-policy="CALLER_RUNS"/>
最后,keep-alive
setting 确定线程的时间限制(以秒为单位)
在停止之前可能会保持空闲状态。如果线程数超过核心数
当前在池中,在等待此时间而不处理任务后,超出
线程停止。时间值为零会导致过多的线程停止
在执行任务后立即执行,而不在任务队列中保留后续工作。
以下示例将keep-alive
值设置为 2 分钟:
<task:executor
id="executorWithKeepAlive"
pool-size="5-25"
keep-alive="120"/>
'scheduled-tasks' 元素
Spring 的 task 命名空间最强大的功能是支持配置
要在 Spring Application Context 中调度的任务。这遵循一种方法
类似于 Spring 中的其他“方法调用程序”,例如 JMS 名称空间提供的
用于配置消息驱动的 POJO。基本上,ref
attribute 可以指向任何
Spring 管理的对象和method
attribute 提供要
在该对象上调用。下面的清单显示了一个简单的示例:
<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 包含其触发器元数据的配置。在前面的示例中,
该元数据定义了一个周期性触发器,该触发器具有固定的延迟,指示
毫秒,以便在每个任务执行完成后等待。另一种选择是fixed-rate
,指示该方法应运行的频率,而不管多长时间
任何以前的执行都需要。此外,对于两者fixed-delay
和fixed-rate
tasks 中,您可以指定一个 'initial-delay' 参数,指示
毫秒,以便在首次执行方法之前等待。如需更多控制,
您可以改为提供cron
属性来提供 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 字段中,
L
代表该月的最后一天。 如果后跟负偏移量(即L-n
),这意味着n
当月的第 1 天到最后一天. -
在 day-of-week 字段中,
L
代表一周的最后一天。 如果以数字或三个字母的名称 (dL
或DDDL
),这意味着一周的最后一天 (d
或DDD
).
-
-
day-of-month 字段可以是
nW
,代表最接近的工作日与当月的日期n
. 如果n
落在星期六,这将产生它之前的星期五。 如果n
落在星期日,则会产生 Monday after,如果n
是1
并落在 星期六(即:1W
代表该月的第一个工作日)。 -
如果 day-of-month 字段为
LW
,它表示该月的最后一个工作日。 -
day-of-week 字段可以是
d#n
(或DDD#n
),代表这n
一周中的第 1 天d
(或DDD
).
以下是一些示例:
Cron 表达式 | 意义 |
---|---|
|
每天每小时的顶部 |
|
每十秒 |
|
每天 8 点、9 点和 10 点 |
|
每天上午 6:00 和晚上 7:00 |
|
每天 8:00、8:30、9:00、9:30、10:00 和 10:30 |
|
工作日朝九晚五 |
|
每个圣诞节的午夜 |
|
每月的最后一天午夜 |
|
每月倒数第三天的午夜 |
|
每月最后一个星期五午夜 |
|
每月最后一个星期四午夜 |
|
每月第一个工作日的午夜 |
|
每月最后一个工作日午夜 |
|
每月第二个星期五的午夜 |
|
每月第一个星期一的午夜 |
使用 Quartz 调度程序
Quartz用途Trigger
,Job
和JobDetail
对象实现对所有
工作种类。有关 Quartz 背后的基本概念,请参阅 Quartz Web 站点。为方便起见,Spring
提供了几个类,这些类简化了在基于 Spring 的应用程序中使用 Quartz。
使用JobDetailFactoryBean
QuartzJobDetail
对象包含运行作业所需的所有信息。Spring
提供JobDetailFactoryBean
,它为 XML 提供 Bean 样式属性
配置目的。请考虑以下示例:
<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
还可以获得
其 properties 从 Job 数据映射到 Job 实例的 properties 中。因此,在
以下示例中,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
}
}
作业数据映射中的所有其他属性也可供您使用。
通过使用name 和group properties 中,您可以修改 name 和 group
的工作。默认情况下,作业的名称与 Bean 名称匹配
的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"/>
通过使用MethodInvokingJobDetailFactoryBean
,您无需创建单行作业
,它仅调用一个方法。您只需创建实际的业务对象,并且
关联 Detail 对象。
默认情况下,Quartz 作业是无状态的,因此 Job 可能会干扰
彼此之间。如果您为同一JobDetail
,这是可能的
第二个作业在第一个作业完成之前启动。如果JobDetail
类
实现Stateful
接口,则不会发生这种情况:第二个作业不会启动
在第一个完成之前。
要使 Job 从MethodInvokingJobDetailFactoryBean
是非并发的,
将concurrent
flag 设置为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
用于在特定对象上调用方法。当然,我们仍然需要将
工作本身。这是通过使用触发器和SchedulerFactoryBean
.几个
触发器在 Quartz 中可用,Spring 提供两个 QuartzFactoryBean
具有方便默认值的实现:CronTriggerFactoryBean
和SimpleTriggerFactoryBean
.
需要安排触发器。Spring 提供了一个SchedulerFactoryBean
这暴露了
触发器设置为 Properties。SchedulerFactoryBean
使用
那些触发器。
下面的清单使用了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>
更多属性可用于SchedulerFactoryBean
,例如
作业详细信息、用于自定义 Quartz 的属性以及 Spring 提供的 JDBC DataSource。看
这SchedulerFactoryBean
javadoc 了解更多信息。
SchedulerFactoryBean 还可以识别quartz.properties file 中,
基于 Quartz 属性键,与常规 Quartz 配置一样。请注意,许多SchedulerFactoryBean 设置与属性文件中的常见 Quartz 设置交互;
因此,建议不要在这两个级别都指定值。例如,不要设置
“org.quartz.jobStore.class”属性(如果您打算依赖 Spring 提供的 DataSource),
或指定org.springframework.scheduling.quartz.LocalDataSourceJobStore 变体,其中
是标准的成熟替代品org.quartz.impl.jdbcjobstore.JobStoreTX . |