avatar


19.Quartz

简介

Quartz:任务调度框架。
官网:http://www.quartz-scheduler.org

所谓的任务调度,其实就是我们想在什么时候做什么事情?

  • 我们想做的事情就是Job(任务)。
  • 我们期望的时候就是Trigger(触发器)。
  • 最后还需要一个Scheduler(调度器),将其整合起来。

案例

新建一个最简单的Maven工程,引入Quartz的Jar包。

1
2
3
4
5
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.2</version>
</dependency>

有些资料会说还需要引入quartz-jobs

1
2
3
4
5
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.3.2</version>
</dependency>

实际上,这个包不一定需要引用。
这个包内部,基本上是一些预设好的Job。
quartz-jobs

创建HelloJob任务类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.kakawanyifan.job;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import java.time.LocalDateTime;

public class HelloJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println(LocalDateTime.now());
}
}

创建主方法的类,在该类中定义Trigger(触发器)以及Scheduler(调度器)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.kakawanyifan;

import com.kakawanyifan.job.HelloJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

public class HelloSchedule {
public static void main(String[] args) throws SchedulerException {

// 调度器 从工厂中获取调度实例
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

// 任务实例
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class).
withIdentity("job1", "group1").
build();

// 触发器
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever()).build();

// 让调度器关联任务和触发器
scheduler.scheduleJob(jobDetail,trigger);

// 启动
scheduler.start();
}
}

运行任务调度类,运行结果:

1
2
3
4
5
2022-12-10T08:24:57.480
2022-12-10T08:25:02.385
2022-12-10T08:25:07.384

【部分运行结果略】

解释说明:

  • 每一个Job必须实现org.quartz.job接口,并实现execute()方法。
  • JobDetail,定义任务实例。在上文,JobDetail实例是通过JobBuilder创建的。
  • JobBuilder,用于创建一个任务实例。
  • TriggerBuilder,用于创建触发器实例。
  • scheduler的方法除了start()shutdown(),还有standby()(暂停操作)。

除了上文的创建scheduler的方法,我们还可以通过如下的方法创建:

1
2
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();

上文的方法属于静态工厂,这种方法属于实例工厂。
关于静态工程和实例工厂,我们在《15.Spring Framework [1/2]》有过讨论。

生命周期

每次调度器执行Job时,在调用execute方法前都会创建一个新的Job实例,当调用完成后,关联的Job对象实例会被释放,释放的实例会被垃圾回收机制回收。

我们可以验证一下,修改HelloJob类,新增构造方法,并在构造方法中打印HelloJob Constructor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.kakawanyifan.job;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import java.time.LocalDateTime;

public class HelloJob implements Job {

public HelloJob() {
System.out.println("HelloJob Constructor");
}

@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println(LocalDateTime.now());
}
}

运行结果:

1
2
3
4
5
6
7
8
HelloJob Constructor : 2022-12-10T21:20:09.829
2022-12-10T21:20:09.831
HelloJob Constructor : 2022-12-10T21:20:14.776
2022-12-10T21:20:14.776
HelloJob Constructor : 2022-12-10T21:20:19.780
2022-12-10T21:20:19.780

【部分运行结果略】

特别的,如果我们创建一个有参构造方法,并且我们不创建无参构造方法呢?
示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.kakawanyifan.job;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.PersistJobDataAfterExecution;

import java.time.LocalDateTime;

@PersistJobDataAfterExecution
public class HelloJob implements Job {

private String k1;

public HelloJob(String k1) {
System.out.println("HelloJob Constructor");
this.k1 = k1;
}

@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {


System.out.println(LocalDateTime.now());
}
}

运行结果:

1
【无运行结果】

程序不会运行。
因为默认使用的是无参构造方法。

所以,我们在《2.面向对象》说:“无论是否使用,都手工书写无参数构造方法”。

JobExecutionContext

当Scheduler调用一个Job,就会将JobExecutionContext传递给Job的execute()方法。

获取Job的明细数据

Job能通过JobExecutionContext对象访问到Quartz运行时候的环境以及Job本身的明细数据。例如:GroupNameClass、当前任务执行时间(.getFireTime())、下一次任务执行时间(.getNextFireTime())等。

获取GroupNameClass,示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.kakawanyifan.job;

import org.quartz.*;

import java.time.LocalDateTime;

public class HelloJob implements Job {

@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {

// 获取JonDetail
JobKey jobKey = jobExecutionContext.getJobDetail().getKey();
System.out.println("jobKey.getGroup() : " + jobKey.getGroup());
System.out.println("jobKey.getName() : " + jobKey.getName());
System.out.println("jobKey.getClass() : " + jobKey.getClass());

TriggerKey triggerKey = jobExecutionContext.getTrigger().getKey();
System.out.println("triggerKey.getGroup() : " + triggerKey.getGroup());
System.out.println("triggerKey.getName() : " + triggerKey.getName());
System.out.println("triggerKey.getClass() : " + triggerKey.getClass());

System.out.println(LocalDateTime.now());
}
}

运行结果:

1
2
3
4
5
6
7
8
9
10

jobKey.getGroup() : group1
jobKey.getName() : job1
jobKey.getClass() : class org.quartz.JobKey
triggerKey.getGroup() : group1
triggerKey.getName() : trigger1
triggerKey.getClass() : class org.quartz.TriggerKey
2022-12-10T21:32:20.901

【部分运行结果略】

获取当前任务执行时间(.getFireTime())、下一次任务执行时间(.getNextFireTime()),示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.kakawanyifan.job;

import org.quartz.*;

import java.time.LocalDateTime;
import java.util.Date;

public class HelloJob implements Job {

@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {

Date fireTime = jobExecutionContext.getFireTime();
System.out.println("当前任务执行时间:" + fireTime);

Date nextFireTime = jobExecutionContext.getNextFireTime();
System.out.println("下一次任务执行时间:" + nextFireTime);

System.out.println(LocalDateTime.now());
}
}

运行结果:

1
2
3
4
5
6
7
8
9
10
11
当前任务执行时间:Tue Jan 10 21:55:57 CST 2023
下一次任务执行时间:Tue Jan 10 21:56:02 CST 2023
2022-12-10T21:55:57.939
当前任务执行时间:Tue Jan 10 21:56:02 CST 2023
下一次任务执行时间:Tue Jan 10 21:56:07 CST 2023
2022-12-10T21:56:02.880
当前任务执行时间:Tue Jan 10 21:56:07 CST 2023
下一次任务执行时间:Tue Jan 10 21:56:12 CST 2023
2022-12-10T21:56:07.880

【部分运行结果略】

传递参数

  • 可以在定义JobDetail实例时,通过.usingJobData(jobDataMap)进行参数传递。
  • 可以在Job中通过jobExecutionContext.getJobDetail().getJobDataMap()解析参数。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.kakawanyifan;

import com.kakawanyifan.job.HelloJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

public class App {
public static void main(String[] args) throws SchedulerException {

// 调度器 从工厂中获取调度实例
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("jobK1","jjj-1");
jobDataMap.put("jobK2",2);

// 任务实例
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
.withIdentity("job1", "group1")
.usingJobData(jobDataMap)
.build();

JobDataMap triggerDataMap = new JobDataMap();
triggerDataMap.put("triggerK1","ttt-1");
triggerDataMap.put("triggerK2",2.0);

// 触发器
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
.usingJobData(triggerDataMap)
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever()).build();

// 让调度器关联任务和触发器
scheduler.scheduleJob(jobDetail,trigger);

// 启动
scheduler.start();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.kakawanyifan.job;

import org.quartz.*;

import java.time.LocalDateTime;

public class HelloJob implements Job {

@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {

// 获取JonDetail
JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();
for (String jobDataMapKey: jobDataMap.keySet()) {
System.out.println(jobDataMapKey + " : " + jobDataMap.get(jobDataMapKey));
}

JobDataMap triggerDataMap = jobExecutionContext.getTrigger().getJobDataMap();
for (String triggerDataMapKey: triggerDataMap.keySet()) {
System.out.println(triggerDataMapKey + " : " + triggerDataMap.get(triggerDataMapKey));
}

System.out.println(LocalDateTime.now());
}
}

运行结果:

1
2
3
4
5
6
7
8

jobK2 : 2
jobK1 : jjj-1
triggerK1 : ttt-1
triggerK2 : 2.0
2022-12-10T21:42:37.001

【部分运行结果略】

除了上文的getJobDataMap,也可以采取如下的方式传递参数,解析方法不变。

其他方法

Setter方法

如果我们在Job类中添加成员变量及其Setter方法对应JobDataMap的键值,Quartz默认的JobFactory实现类在初始化JobDetail对象时会自动地调用这些Setter方法。

这样,解析参数会更方便一些,因为这些参数都作为了Job类的成员变量。

例子

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.kakawanyifan.job;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import java.time.LocalDateTime;

public class HelloJob implements Job {

private String k1;
private Integer k2;

public void setK1(String k1) {
System.out.println("setK1 : " + k1);
this.k1 = k1;
}

public void setK2(Integer k2) {
System.out.println("setK2 : " + k2);
this.k2 = k2;
}

@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {

System.out.println(k1);
System.out.println(k2);

System.out.println(LocalDateTime.now());
}
}

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.kakawanyifan;

import com.kakawanyifan.job.HelloJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

public class HelloSchedule {
public static void main(String[] args) throws SchedulerException {

// 调度器 从工厂中获取调度实例
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("k1","kkk-1");
jobDataMap.put("k2",2);

// 任务实例
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
.withIdentity("job1", "group1")
.usingJobData(jobDataMap)
.build();


JobDataMap triggerDataMap = new JobDataMap();
triggerDataMap.put("k1","kkk-vvv-1");
triggerDataMap.put("k2",100);

// 触发器
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
.usingJobData(triggerDataMap)
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever()).build();

// 让调度器关联任务和触发器
scheduler.scheduleJob(jobDetail,trigger);

// 启动
scheduler.start();
}
}

运行结果:

1
2
3
4
5
setK1 : kkk-vvv-1
setK2 : 100
kkk-vvv-1
100
2022-12-11T08:00:57.275

覆盖规则

如果遇到同名的keyTrigger中的.usingJobData()会覆盖JobDetail中的.usingJobData()

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.kakawanyifan;

import com.kakawanyifan.job.HelloJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

public class HelloSchedule {
public static void main(String[] args) throws SchedulerException {

// 调度器 从工厂中获取调度实例
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

JobDataMap triggerDataMap = new JobDataMap();
triggerDataMap.put("k1","kkk-vvv-1");
triggerDataMap.put("k2",100);

// 触发器
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
.usingJobData(triggerDataMap)
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever()).build();

JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("k1","kkk-1");
jobDataMap.put("k2",2);

// 任务实例
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
.withIdentity("job1", "group1")
.usingJobData(jobDataMap)
.build();

// 让调度器关联任务和触发器
scheduler.scheduleJob(jobDetail,trigger);

// 启动
scheduler.start();
}
}

运行结果:

1
2
3
4
5
setK1 : kkk-vvv-1
setK2 : 100
kkk-vvv-1
100
2022-12-11T08:03:24.843

有状态的Job

所谓的有状态的Job是指多次Job调用期间可以持有一些状态信息,这些状态信息存储在JobDataMap中。
在实现方法上,在Job类上加上@PersistJobDataAfterExecution注解即可。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.kakawanyifan.job;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.PersistJobDataAfterExecution;

import java.time.LocalDateTime;

@PersistJobDataAfterExecution
public class HelloJob implements Job {

private String k1;
private Integer k2;

public void setK1(String k1) {
System.out.println("setK1 : " + k1);
this.k1 = k1;
}

public void setK2(Integer k2) {
System.out.println("setK2 : " + k2);
this.k2 = k2;
}

@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {

System.out.println("k1 :" + k1);
System.out.println("k2 :" + k2);

k2 = k2 + 1;

jobExecutionContext.getJobDetail().getJobDataMap().put("k2", k2);

System.out.println(LocalDateTime.now());
}
}

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.kakawanyifan;

import com.kakawanyifan.job.HelloJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

public class HelloSchedule {
public static void main(String[] args) throws SchedulerException {

// 调度器 从工厂中获取调度实例
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

// 任务实例
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
.withIdentity("job1", "group1")
.usingJobData("k1","kv1")
.usingJobData("k2",1)
.build();

// 触发器
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever()).build();

// 让调度器关联任务和触发器
scheduler.scheduleJob(jobDetail,trigger);

// 启动
scheduler.start();
}
}

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
setK1 : kv1
setK2 : 1
k1 :kv1
k2 :1
2022-12-11T08:07:33.029
setK1 : kv1
setK2 : 2
k1 :kv1
k2 :2
2022-12-11T08:07:37.933
setK1 : kv1
setK2 : 3
k1 :kv1
k2 :3
2022-12-11T08:07:42.934

有些资料说,添加@PersistJobDataAfterExecution注解后,不会每次调用都创建一个新的实例,这个说法是错误的。实际上,还是每次都创建了新的实例。

(通过注解名字,我们也能知道,被持久化的,是JobData,而不是JobDetail。)

我们可以创建一个无参的构造方法,并打印一行内容,进行验证。示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.kakawanyifan.job;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.PersistJobDataAfterExecution;

import java.time.LocalDateTime;

@PersistJobDataAfterExecution
public class HelloJob implements Job {

public HelloJob() {
System.out.println("HelloJob...");
}

private String k1;
private Integer k2;

public void setK1(String k1) {
System.out.println("setK1 : " + k1);
this.k1 = k1;
}

public void setK2(Integer k2) {
System.out.println("setK2 : " + k2);
this.k2 = k2;
}

@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {

System.out.println("k1 :" + k1);
System.out.println("k2 :" + k2);

k2 = k2 + 1;

jobExecutionContext.getJobDetail().getJobDataMap().put("k2", k2);

System.out.println(LocalDateTime.now());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.kakawanyifan;

import com.kakawanyifan.job.HelloJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

public class HelloSchedule {
public static void main(String[] args) throws SchedulerException {

// 调度器 从工厂中获取调度实例
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

// 任务实例
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
.withIdentity("job1", "group1")
.usingJobData("k1","kv1")
.usingJobData("k2",1)
.build();

// 触发器
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever()).build();

// 让调度器关联任务和触发器
scheduler.scheduleJob(jobDetail,trigger);

// 启动
scheduler.start();
}
}

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
HelloJob...
setK1 : kv1
setK2 : 1
k1 :kv1
k2 :1
2022-12-11T08:10:17.520
HelloJob...
setK1 : kv1
setK2 : 2
k1 :kv1
k2 :2
2022-12-11T08:10:22.459
HelloJob...
setK1 : kv1
setK2 : 3
k1 :kv1
k2 :3
2022-12-11T08:10:27.458

Trigger

Quartz有多种触发器,使用最多的是SimpleTrigger和CronTrigger。

Trigger的属性

我们可以在Trigger中定义startTime(开始时间)和endTime(结束时间)。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.kakawanyifan.job;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.Trigger;

import java.time.LocalDateTime;


public class HelloJob implements Job {

@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {

// 定义工作任务内容
System.out.println(LocalDateTime.now());

// 获取jobKey、startTime、endTime
Trigger trigger = jobExecutionContext.getTrigger();
System.out.println("name : " +trigger.getJobKey().getName());
System.out.println("group : " + trigger.getJobKey().getGroup());
System.out.println("任务开始时间 : " + trigger.getStartTime());
System.out.println("任务结束时间 : " + trigger.getEndTime());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package com.kakawanyifan;

import com.kakawanyifan.job.HelloJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

import java.util.Date;

public class HelloSchedule {
public static void main(String[] args) throws SchedulerException {
// 1:从工厂中获取任务调度的实例
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

// 定义日期
Date startDate = new Date();
// 启动任务,任务在当前时间3秒后执行
startDate.setTime(startDate.getTime() + 3000);
// 定义日期
Date endDate = new Date();
// 结束任务,任务在当前时间10秒后停止
endDate.setTime(endDate.getTime() + 10000);

// 2:定义一个任务调度实例,将该实例与HelloJob绑定,任务类需要实现Job接口
JobDetail job = JobBuilder.newJob(HelloJob.class)
.withIdentity("job1", "group1") // 定义该实例唯一标识
.usingJobData("message", "打印日志")
.build();

// 3:定义触发器 ,马上执行, 然后每5秒重复执行一次
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1") // 定义该实例唯一标识
.startAt(startDate)
.endAt(endDate)
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever()) // 每5秒执行一次
.usingJobData("message", "simple触发器")
.build();

// 4:使用触发器调度任务的执行
scheduler.scheduleJob(job, trigger);

// 5:开启
scheduler.start();
// 关闭
// scheduler.shutdown();
}
}

运行结果:

1
2
3
4
5
2022-12-11T08:35:50.408
name : job1
group : group1
任务开始时间 : Wed Jan 11 08:35:50 CST 2023
任务结束时间 : Wed Jan 11 08:35:57 CST 2023

SimpleTrigger

SimpleTrigger的使用场景:在指定的时间启动,且以指定的间隔时间重复执行若干次。

例子一:在指定的时间启动,只执行一次。 示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.kakawanyifan.job;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import java.time.LocalDateTime;


public class HelloJob implements Job {

@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {

// 定义工作任务内容
System.out.println(LocalDateTime.now());

}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.kakawanyifan;

import com.kakawanyifan.job.HelloJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

import java.util.Date;

public class HelloSchedule {
public static void main(String[] args) throws SchedulerException {
// 1:从工厂中获取任务调度的实例
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

// 定义日期
Date startDate = new Date();
// 启动任务,任务在当前时间 10秒后执行
startDate.setTime(startDate.getTime() + 10000);

// 2:定义一个任务调度实例,将该实例与HelloJob绑定,任务类需要实现Job接口
JobDetail job = JobBuilder.newJob(HelloJob.class)
.build();

Trigger trigger = TriggerBuilder.newTrigger()
.startAt(startDate)
.build();

// 4:使用触发器调度任务的执行
scheduler.scheduleJob(job, trigger);

// 5:开启
scheduler.start();
// 关闭
// scheduler.shutdown();
}
}

运行结果:

1
2022-12-12T07:49:04.813

例子二:在指定的时间启动,每次间隔5秒,永久执行。 示例代码:

1
2
3
4
Trigger trigger = TriggerBuilder.newTrigger()
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever()) // 每5秒执行一次
.startAt(startDate)
.build();

例子三:在指定的时间启动,每次间隔5秒,永久执行。并在指定的时间结束 示例代码:

1
2
3
4
5
Trigger trigger = TriggerBuilder.newTrigger()
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever()) // 每5秒执行一次
.startAt(startDate)
.endAt(endDate)
.build();

CronTrigger

CronTriggers:基于日历的作业调度器。

使用CronTrigger,你可以指定诸如"每个周五12:00"、"每周一、周三、周五的上午9:00到上午10:00之间每隔五分钟"等,这样日程安排来触发。

Cron表达式

这不是我们第一次讨论Cron表达式,在《Linux操作系统使用入门:2.命令》,我们也讨论过Cron表达式。

Linux中的Cron表达式一共5位,从左到右,分别是:

  1. 分钟
  2. 小时
  3. 日期
  4. 月份
  5. 星期

QuartZ中的Cron表达式,有7位,从左到右,分别是

  1. 分钟
  2. 小时
  3. 日期
  4. 月份
  5. 星期
  6. 年(可选)

(粗体字标识的,是相较于Linux中的Cron表达式,多的两位。)

注意:

  • 星期:用1到7来表示(1代表星期天),或者用字符串SUNMONTUEWEDTHUFRISAT来表示。
    • 这部分与在Linux中不同,Linux中1代表星期一。
  • 月份,用1到12来表示,或者用字符串JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC来表示。

《Linux操作系统使用入门:2.命令》,我们还讨论了一些特殊值:

特殊字符 含义 示例
* 所有可能的值 在月域中,*表示每个月;在星期域中,*表示星期的每一天。
, 列出枚举值 在分钟域中,5,20表示分别在5分钟和20分钟触发一次。
- 范围 在分钟域中,5-20表示从5分钟到20分钟之间每隔一分钟触发一次。
/ 指定数值的增量 在分钟域中,0/15表示从第0分钟开始,每15分钟,3/20表示从第3分钟开始,每20分钟。

在Cron中有更多的特殊值:

特殊字符 含义 示例
? 表示不指定值 例如,"月份中的日期"和"星期中的日期"这两个元素是互斥的,因此应该通过设置一个问号?来表明不想设置某个字段
L 最后一个 在day-of-month中,L表示这个月的最后一天,例如一月的31日、二月的28日。在day-of-week中,表示7或者SAT。但是如果在day-of-week中,这个字符跟在别的值后面,则表示"当月的最后一个的周XXX",例如:6L或者FRIL,表示当月的最后一个周五。注意,使用L选项时,不要指定列表或者值范用。
W 指定离给定日期最近的工作日(周一到周五) 在日期域中5W,如果5日是星期六,则将在最近的工作日星期五,即4日触发。如果5日是星期天,则将在最近的工作日星期一,即6日触发;如果5日在星期一到星期五中的一天,则就在5日触发。
# 确定每个月第几个星期几,仅星期域支持该字符。 在星期域中,4#2表示某月的第二个星期四。

0 0 10,14,16 * * ?
每天10:00、14:00、16:00

最近10次运行时间

1
2
3
4
5
6
7
8
9
10
2022-12-12 10:00:00
2022-12-12 14:00:00
2022-12-12 16:00:00
2022-12-13 10:00:00
2022-12-13 14:00:00
2022-12-13 16:00:00
2022-12-14 10:00:00
2022-12-14 14:00:00
2022-12-14 16:00:00
2022-12-15 10:00:00

0 0/30 9-12 * * ?
每天9时到12时,0分0秒开始每隔30分钟发送一次。

1
2
3
4
5
6
7
8
9
10
2022-12-12 09:00:00
2022-12-12 09:30:00
2022-12-12 10:00:00
2022-12-12 10:30:00
2022-12-12 11:00:00
2022-12-12 11:30:00
2022-12-12 12:00:00
2022-12-12 12:30:00
2022-12-13 09:00:00
2022-12-13 09:30:00
  • 注意:12:30:00也会运行。

0 0 12 ? * WED
每个星期三,12:00:00

1
2
3
4
5
6
7
8
9
10
2022-12-18 12:00:00
2022-12-25 12:00:00
2023-02-01 12:00:00
2023-02-08 12:00:00
2023-02-15 12:00:00
2023-02-22 12:00:00
2023-03-01 12:00:00
2023-03-08 12:00:00
2023-03-15 12:00:00
2023-03-22 12:00:00

0 0 12 * * ?
每天,12:00:00

1
2
3
4
5
6
7
8
9
10
2022-12-12 12:00:00
2022-12-13 12:00:00
2022-12-14 12:00:00
2022-12-15 12:00:00
2022-12-16 12:00:00
2022-12-17 12:00:00
2022-12-18 12:00:00
2022-12-19 12:00:00
2022-12-20 12:00:00
2022-12-21 12:00:00

0 15 10 ? * *
每天,10:15:00

1
2
3
4
5
6
7
8
9
10
2022-12-12 10:15:00
2022-12-13 10:15:00
2022-12-14 10:15:00
2022-12-15 10:15:00
2022-12-16 10:15:00
2022-12-17 10:15:00
2022-12-18 10:15:00
2022-12-19 10:15:00
2022-12-20 10:15:00
2022-12-21 10:15:00

0 15 10 * * ?
每天,10:15:00

1
2
3
4
5
6
7
8
9
10
2022-12-12 10:15:00
2022-12-13 10:15:00
2022-12-14 10:15:00
2022-12-15 10:15:00
2022-12-16 10:15:00
2022-12-17 10:15:00
2022-12-18 10:15:00
2022-12-19 10:15:00
2022-12-20 10:15:00
2022-12-21 10:15:00

0 15 10 * * ? *
每天,10:15:00

1
2
3
4
5
6
7
8
9
10
2022-12-12 10:15:00
2022-12-13 10:15:00
2022-12-14 10:15:00
2022-12-15 10:15:00
2022-12-16 10:15:00
2022-12-17 10:15:00
2022-12-18 10:15:00
2022-12-19 10:15:00
2022-12-20 10:15:00
2022-12-21 10:15:00

0 15 10 * * ? 2099
2099年,10:15:00

1
2
3
4
5
6
7
8
9
10
2099-01-01 10:15:00
2099-01-02 10:15:00
2099-01-03 10:15:00
2099-01-04 10:15:00
2099-01-05 10:15:00
2099-01-06 10:15:00
2099-01-07 10:15:00
2099-01-08 10:15:00
2099-01-09 10:15:00
2099-01-10 10:15:00

0 * 14 * * ?
每天,14点,的每一分钟的0秒。

1
2
3
4
5
6
7
8
9
10
2022-12-12 14:00:00
2022-12-12 14:01:00
2022-12-12 14:02:00
2022-12-12 14:03:00
2022-12-12 14:04:00
2022-12-12 14:05:00
2022-12-12 14:06:00
2022-12-12 14:07:00
2022-12-12 14:08:00
2022-12-12 14:09:00

0 0/55 14 * * ?
每天,14点,0分0秒。55分钟后。

1
2
3
4
5
6
7
8
9
10
2022-12-12 14:00:00
2022-12-12 14:55:00
2022-12-13 14:00:00
2022-12-13 14:55:00
2022-12-14 14:00:00
2022-12-14 14:55:00
2022-12-15 14:00:00
2022-12-15 14:55:00
2022-12-16 14:00:00
2022-12-16 14:55:00

0 0/55 14,18 * * ?
每天,14点,0分0秒。55分钟后。以及18点,0分0秒。55分钟后。

1
2
3
4
5
6
7
8
9
10
2022-12-12 14:00:00
2022-12-12 14:55:00
2022-12-12 18:00:00
2022-12-12 18:55:00
2022-12-13 14:00:00
2022-12-13 14:55:00
2022-12-13 18:00:00
2022-12-13 18:55:00
2022-12-14 14:00:00
2022-12-14 14:55:00

0 0-5 14 * * ?
每天,14点,0分到5分,0秒。

1
2
3
4
5
6
7
8
9
10
2022-12-12 14:00:00
2022-12-12 14:01:00
2022-12-12 14:02:00
2022-12-12 14:03:00
2022-12-12 14:04:00
2022-12-12 14:05:00
2022-12-13 14:00:00
2022-12-13 14:01:00
2022-12-13 14:02:00
2022-12-13 14:03:00

0 10 14 ? 3 WED
每年三月的星期三的,14:10

1
2
3
4
5
6
7
8
9
10
2023-03-01 14:10:00
2023-03-08 14:10:00
2023-03-15 14:10:00
2023-03-22 14:10:00
2023-03-29 14:10:00
2024-03-06 14:10:00
2024-03-13 14:10:00
2024-03-20 14:10:00
2024-03-27 14:10:00
2025-03-05 14:10:00

0 15 10 ? * MON-FRI
周一至周五的,10:15触发

1
2
3
4
5
6
7
8
9
10
2022-12-12 10:15:00
2022-12-13 10:15:00
2022-12-16 10:15:00
2022-12-17 10:15:00
2022-12-18 10:15:00
2022-12-19 10:15:00
2022-12-20 10:15:00
2022-12-23 10:15:00
2022-12-24 10:15:00
2022-12-25 10:15:00

0 15 10 15 * ?
每月15日,10:15:00

1
2
3
4
5
6
7
8
9
10
2022-12-15 10:15:00
2023-02-15 10:15:00
2023-03-15 10:15:00
2023-04-15 10:15:00
2023-05-15 10:15:00
2023-06-15 10:15:00
2023-07-15 10:15:00
2023-08-15 10:15:00
2023-09-15 10:15:00
2023-10-15 10:15:00

0 15 10 L * ?
每月最后一日的上午10:15触发

1
2
3
4
5
6
7
8
9
10
2022-12-31 10:15:00
2023-02-28 10:15:00
2023-03-31 10:15:00
2023-04-30 10:15:00
2023-05-31 10:15:00
2023-06-30 10:15:00
2023-07-31 10:15:00
2023-08-31 10:15:00
2023-09-30 10:15:00
2023-10-31 10:15:00

0 15 10 ? * 6L
每月的最后一个星期五上午10:15触发

1
2
3
4
5
6
7
8
9
10
2022-12-27 10:15:00
2023-02-24 10:15:00
2023-03-31 10:15:00
2023-04-28 10:15:00
2023-05-26 10:15:00
2023-06-30 10:15:00
2023-07-28 10:15:00
2023-08-25 10:15:00
2023-09-29 10:15:00
2023-10-27 10:15:00

0 15 10 ? * 6#3
每月的第三个星期五上午10:15触发

1
2
3
4
5
6
7
8
9
10
2022-12-20 10:15:00
2023-02-17 10:15:00
2023-03-17 10:15:00
2023-04-21 10:15:00
2023-05-19 10:15:00
2023-06-16 10:15:00
2023-07-21 10:15:00
2023-08-18 10:15:00
2023-09-15 10:15:00
2023-10-20 10:15:00

应用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.kakawanyifan;

import com.kakawanyifan.job.HelloJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

public class HelloSchedule {
public static void main(String[] args) throws SchedulerException {
// 1:从工厂中获取任务调度的实例
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

// 2:定义一个任务调度实例,将该实例与HelloJob绑定,任务类需要实现Job接口
JobDetail job = JobBuilder.newJob(HelloJob.class)
.build();

// 3:定义触发器 ,马上执行, 然后每5秒重复执行一次
Trigger trigger = TriggerBuilder.newTrigger()
.withSchedule(CronScheduleBuilder.cronSchedule("0/30 * * * * ?"))
.build();

// 4:使用触发器调度任务的执行
scheduler.scheduleJob(job, trigger);

// 5:开启
scheduler.start();
// 关闭
// scheduler.shutdown();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.kakawanyifan.job;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import java.time.LocalDateTime;


public class HelloJob implements Job {

@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {

// 定义工作任务内容
System.out.println(LocalDateTime.now());

}
}

运行结果:

1
2
3
4
5
2022-12-12T08:32:00.054
2022-12-12T08:32:30.003
2022-12-12T08:33:00.002
2022-12-12T08:33:30.015
2022-12-12T08:34:00.018

Quartz.properties

quartz-2.3.2.jar的内部,org/quartz目录下默认的配置文件quartz.properties,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
org.quartz.scheduler.instanceName: DefaultQuartzScheduler
org.quartz.scheduler.rmi.export: false
org.quartz.scheduler.rmi.proxy: false
org.quartz.scheduler.wrapJobExecutionInUserTransaction: false

org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount: 10
org.quartz.threadPool.threadPriority: 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true

org.quartz.jobStore.misfireThreshold: 60000

org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore

调度器属性:

  • org.quartz.scheduler.instanceName,调度器的实例名。用来区分特定的调度器实例,可以按照功能用途来给调度器起名。
  • org.quartz.scheduler.instanceId,调度器的实例ID。和前者一样,也允许任何字符串,但这个值必须在所有调度器实例中是唯一的,尤其是在一个集群环境中,作为集群的唯一key。假如你想Quartz帮你生成这个值的话,可以设置为AUTO,大多数情况设置为AUTO即可。

线程池属性:

  • org.quartz.threadPool.threadCount,处理Job的线程个数,至少为1,但最多的话最好不要超过100。
  • org.quartz.threadPool.threadPriority,线程的优先级,优先级别高的线程比级别低的线程优先得到执行。最小为1,最大为10,默认为5
  • org.quartz.threadPool.class,线程池的实现类。

作业存储设置:

  • org.quartz.jobStore.class,Job存储实现类,在本文中,Job存储在内存里。

我们也可以在项目的资源下添加quartz.properties文件,去覆盖底层的配置文件。

整合Spring

spring-context-support

需要引入Jar包spring-context-support

1
2
3
4
5
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.3.24</version>
</dependency>

Job类

新建Job类,继承QuartzJobBean。示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.kakawanyifan.job;

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.time.LocalDateTime;

public class QuartzJob extends QuartzJobBean {

@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
System.out.println(LocalDateTime.now());
}
}

配置类

新建一个配置类,配置类中做的事情,其实就是我们上文的App类中的Main方法做的事情。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.kakawanyifan.config;

import com.kakawanyifan.job.QuartzJob;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

public class QuartzJobConfig {
@Bean
public JobDetailFactoryBean jobDetail(){
JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean();
jobDetailFactoryBean.setName("n1");
jobDetailFactoryBean.setGroup("g1");
jobDetailFactoryBean.setJobClass(QuartzJob.class);
return jobDetailFactoryBean;
}

@Bean
public CronTriggerFactoryBean cronTrigger(JobDetailFactoryBean jobDetail){
CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
cronTriggerFactoryBean.setName("n1");
cronTriggerFactoryBean.setGroup("g1");
cronTriggerFactoryBean.setJobDetail(jobDetail.getObject());
cronTriggerFactoryBean.setCronExpression("0/5 * * * * ?");
return cronTriggerFactoryBean;
}

@Bean
public SchedulerFactoryBean scheduler(CronTriggerFactoryBean cronTrigger){
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setTriggers(cronTrigger.getObject());
return schedulerFactoryBean;
}
}

当然,该配置类需要被Import到Spring的配置类中。

  • @Import({JdbcConfig.class, MyBatisConfig.class, QuartzJobConfig.class})

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.kakawanyifan.config;

import org.springframework.context.annotation.*;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@ComponentScan(value = "com.kakawanyifan",
excludeFilters = @ComponentScan.Filter(
type = FilterType.ANNOTATION,
classes = Controller.class
))
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class, MyBatisConfig.class, QuartzJobConfig.class})
@EnableTransactionManagement
public class SpringConfig {

}

XML方式

新建一个XML文件quartzConfig.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<!-- jobDetail -->
<bean id="jobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="name" value="n1" />
<property name="group" value="g1" />
<property name="jobClass" value="com.kakawanyifan.job.QuartzJob" />
</bean>

<!-- Crontrigger -->
<bean id="trigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="name" value="n1" />
<property name="group" value="g1" />
<property name="jobDetail" ref="jobDetail" />
<property name="cronExpression" value="0/5 * * * * ?" />
</bean>

<!-- SchedulerFactoryBean -->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="trigger" />
</list>
</property>
</bean>

</beans>

同样的,该配置文件需要被Import到Spring的配置文件中。

  • <import resource="classpath:quartzConfig.xml" />

有些资料会介绍另一种方法,将Job类的Bean也交给Spring管理,然后在"JobDetailFactoryBean"指定"TargetObject"。
在5版本的Spring中已经找不到这种方法。
在上文我们讨论过Job的生命周期,每次执行都会实例化,而在Spring中默认Bean是单例的。
这样的设计,存在冲突,这或许是这种方法被弃用的原因。

Spring中的定时任务

除了Quartz,Spring中也自带了定时任务。

@EnableScheduling

@EnableScheduling注解,开启定时任务设置。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.kakawanyifan.config;

import org.springframework.context.annotation.*;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@ComponentScan(value = "com.kakawanyifan",
excludeFilters = @ComponentScan.Filter(
type = FilterType.ANNOTATION,
classes = Controller.class
))
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class, MyBatisConfig.class, QuartzJobConfig.class})
@EnableTransactionManagement
@EnableScheduling
public class SpringConfig {

}

@Scheduled

@Scheduled注解,定义定时任务。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.kakawanyifan.job;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

@Component
public class SpringJob {

@Scheduled(cron = "0/10 * * * * ?")
public void runJob(){
System.out.println("SpringJob : " + LocalDateTime.now());
}
}

@Scheduled注解的参数有:

  • cron:cron表达式进行执行。
    @Scheduled(cron = "0/10 * * * * ?")
  • fixedDelay,上次调用结束后,指定毫秒后再执行。
    @Scheduled(fixedDelay = 5000):上次调用结束后5秒再执行。
  • fixedDelayString,与fixedDelay含义相同。只是使用字符串的形式,可以支持占位符。
    @Scheduled(fixedDelayString = "${time.fixedDelay}")
  • fixedRate,无论上次是否结束,指定毫秒之后都会再次执行。
    @Scheduled(fixedRate = 5000):上次开始无论是否结束5秒钟之后会再次执行
  • fixedRateString,与fixedRate含义相同。只是使用字符串的形式,可以支持占位符。
  • initialDelay,初次执行延时时间。
  • initialDelayString,与initialDelay含义相同。只是使用字符串的形式,支持占位符。

XML方式

  • xmlns:task="http://www.springframework.org/schema/task"
  • http://www.springframework.org/schema/task
  • http://www.springframework.org/schema/task/spring-task.xsd

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task.xsd">

【部分代码略】

<bean id="springJob" class="com.kakawanyifan.job.SpringJob"></bean>
<task:scheduler id="springJobScheduler"/>
<task:scheduled-tasks scheduler="springJobScheduler">
<task:scheduled ref="springJob" method="runJob" cron="0/10 * * * * *"/>
</task:scheduled-tasks>

</beans>

除了上文的配置方式,也可以开启注解<task:annotation-driven/>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task.xsd">

【部分代码略】

<task:annotation-driven/>

</beans>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.kakawanyifan.job;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

@Component
public class SpringJob {

@Scheduled(cron = "0/10 * * * * *")
public void runJob(){
System.out.println("SpringJob : " + LocalDateTime.now());
}
}



Quartz中,还有一部分是监听器。有JobListener(任务监听器)、TriggerListener(触发监听器)和SchedulerListener(调度监听器)三种。
在实际工作中,监听器的应用并不多。
我们不讨论。

文章作者: Kaka Wan Yifan
文章链接: https://kakawanyifan.com/10819
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Kaka Wan Yifan

留言板