目的:一篇文章搞懂 Quartz 定时任务框架,从原理 到 Spring Boot 整合 到 CRUD 实际操作
说明: 这篇文章是从 Spring Boot 整合的角度来分析 Quartz 框架,介绍如何整合以及项目中怎么使用。
如果需要从原理角度一步一步分析的文章,请点击这个文章:quartz 原理组件介绍
文章目录
一、Quartz 的作业存储类型
Quartz 的定时任务作业的存储类型有 2 种:内存(默认)、JDBC
1、内存:默认情况下 Quartz 会将任务调度存储在内存中,这种方式性能是最好的,因为内存的速度是最快的。不好的地方就是数据缺乏持久性,程序崩溃或者重新发布的时候,所有运行信息都会丢失。
2、JDBC:存数据库后,可以做单点也可以做集群,当任务多了之后,可以统一进行管理,随时停止、暂停、修改任务。 关闭或者重启服务器,运行的信息都不会丢失。缺点就是运行速度快慢取决于连接数据库的快慢
二、Spring Boot 整合 Quartz(基于内存)
2.1 基本配置
1、引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
-
当我们引入
spring-boot-starter-quartz
的依赖后,spring boot 在启动的时候会自动加载配置类:org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration
,会初始化好调度器(源码:SchedulerFactoryBean
实现InitializingBean
,重写afterPropertiesSet()
里面就有了) -
而且 Scheduler,Spring 默认是启动的,不需要手动启动。
@Autowired private Scheduler scheduler;
2、创建一个任务类继承 Job 类,重写 excute 方法,excute 里面的参数(JobExecutionContext context
)表示:定时任务执行的环境(上下文)
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
@Component
public class MyJob implements Job {
// 编写任务内容
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// 获取JobDetail
JobDetail jobDetail = context.getJobDetail();
// 可以输出任务的一些详情信息
System.out.println("任务名称:" + jobDetail.getKey().getName());
System.out.println("任务分组名称" + jobDetail.getKey().getGroup());
System.out.println("任务类名字:" + jobDetail.getJobClass().getName());
System.out.println("本次任务执行时间:" + context.getFireTime());
System.out.println("下次任务执行时间:" + context.getNextFireTime());
// 还可以通过JobDataMa()存储数据
JobDataMap jobDataMap = jobDetail.getJobDataMap();
int count = jobDataMap.getInt("count");
// 或者:Integer count1 = (Integer) jobDataMap.get("count");
System.out.println("第" + count + "次执行");
// 更新共享数据
jobDataMap.put("count", ++count);
}
}
2.1.1 启动定时任务 方式一
3、创建 JobDetail、创建 Trigger:
- 创建一个配置类(比如:QuartzConfig),然后在配置类中写生成 JobDetail 对象的方法
- 在配置类中写生成 Trigger 对象的方法
@Configuration
public class QuartzConfig {
@Bean
public JobDetail getJobDetail() {
return JobBuilder
// 任务绑定
.newJob(MyJob.class)
.storeDurably()
// 定义唯一标识,区分其他任务
.withIdentity("name任务名", "group任务组")
.usingJobData("count", 1)
.build();
}
@Bean
public Trigger getTrigger() {
// cron表达式
String cron = "*/3 * * * * ?";
return TriggerBuilder
.newTrigger()
// 唯一表示
.withIdentity("trigger1", "group")
// 绑定JobDetail
.forJob(this.getJobDetail())
.withSchedule(CronScheduleBuilder.cronSchedule(cron))
.build();
}
}
运行: 直接启动启动类,即可看到结果:
分析: 这里跟之前不一样,之前使用scheduler.scheduleJob(jobDetail, trigger);
来调度,这里直接将 JobDetail 放到 Trigger 中
注意: JobDetail 中一定要设置为可持久化.storeDurably()
,否则运行报错
2.1.2 启动定时任务 方式二
3、不写配置类:初始化创建 JobDetail 和 Trigger,然后使用 Scheduler 来调用。
@Configuration
public class QuartzConfig2 {
@Autowired
private Scheduler scheduler;
@PostConstruct
public void init() throws SchedulerException {
String cron = "*/3 * * * * ?";
JobDetail jobDetail = JobBuilder
.newJob(MyJob.class)
.withIdentity("name任务名", "group任务组")
.usingJobData("count", 1)
.build();
Trigger trigger = TriggerBuilder
.newTrigger()
// 唯一表示
.withIdentity("trigger1", "group")
.withSchedule(CronScheduleBuilder.cronSchedule(cron))
.build();
scheduler.scheduleJob(jobDetail, trigger);
}
}
引入: 上面的整合运行后,不管是哪种启动定时任务方式,只要重启启动类,都会重新运行定时任务,不会j接着运行上次的。----> 下面这种基于 JDBC 的就可以做到这一点。
二、Spring Boot整合Quartz(基于JDBC)
上面提到基于存数据库后,可以进行统一的管理:随时停止、暂停、修改任务。其实这就是动态调度。
2.1 动态调度
1、动态定时任务:即定时任务的动态调度,可以根据需求自由进行任务的生成、暂停、恢复、删除和更新的操作。Quartz 本身没有提供动态调度功能,需要自己根据相关 API 开发(下面会提到)
2、使用场景:
-
订单成功后自动发送短信通知
-
每日推送功能引发用户不满,不再定时推送
-
每次下班前30分钟发送工作报表,遇节假日暂停发送
3、Scheduler 调度器常用 API
API | 描述 |
---|---|
scheduler.scheduleJob(jobDetail, trigger) |
生成一个新的定时任务 |
scheduler.pauseJob(jobKey) |
暂停一个定时任务,参数 jobKey 代表该任务的唯一标识 |
scheduler.resumeJob(jobKey) |
恢复一个定时任务,参数 jobKey 代表该任务的唯一标识 |
scheduler.deleteJob(jobKey) |
删除一个定时任务,参数 jobKey 代表该任务的唯一标识 |
scheduler.rescheduleJob(triggerKey, trigger) |
修改一个定时任务 |
scheduler.triggerJob(jobKey) |
执行一次定时任务 |
2.2 关于JobKey类
JobKey
类,用于唯一标识一个调度任务(Job
)。在 Quartz 中,任务是通过 JobKey
来识别和管理的。具体来说:JobKey
是 Quartz 中用于标识和引用任务的核心对象。
JobKey 的核心作用就是标识任务并提供对任务的访问方式。它就像任务的身份证,确保你能准确地访问、操作任务,避免任务名称重复或冲突。
1、 JobKey 的定义
JopKey 类主要由两个属性组成:
- 任务名称:标识任务的名称,通常是唯一的字符串。
- 任务组名:任务所属的组名,类似于任务的类别,任务组可以包含多个任务,任务组名也应该是唯一的
2、 JobKey 的构造
两种方式:
-
通过名称和组名构造:
JobKey jobKey = new JobKey("jobName","jobGroup");
-
通过唯一标识符构造: 如果想避免重复的名称/组组合,可以使用
JobKey
的静态方法:JobKey jobKey = JobKey.jobKey("jobName", "jobGroup");
3、 JobKey 在调度器中的应用
-
获取任务的触发器(Trigger):
// 可以通过 Jobkey 获取与任务关联的所有触发器 Set<Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
-
获取任务的详细信息(JobDetail):
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
-
获取任务的状态(TriggerState):
TriggerState triggerState = scheduler.getTriggerState(triggerKey);
4、 JobKey 与 TriggerKey 的关系
- JobKey 标识一个任务(Job),而 TriggerKey 标识与该任务关联的触发器(Trigger)
- 一个 Job 可以有多个 Trigger,但是每个 Trigger 只能关联一个 Job
5、 JobKey 和 任务分组
通过 JobKey
中的 jobGroup
属性,可以将任务按组划分,使得任务管理更加有组织。任务组的好处包括:
- 分组管理:可以按任务组来管理任务,查询某个组内的所有任务。
- 按组删除任务:可以按组来批量删除任务。
- 任务分隔:将不同类别的任务分隔开来,便于管理。
2.3 设置yml配置文件
1、引入依赖,并配置 yml 文件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
spring:
datasource:
# 数据库配置
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/gxxxbixxxxaxx?useUnicode=true&characterEncoding=UTF-8
username: xxx
password: xxx
# quartz配置
quartz:
# quartz任务存储类型:jdbc或memory
job-store-type: jdbc
# 是否等待任务执行完毕后,容器才会关闭
wait-for-jobs-to-complete-on-shutdown: false
# 配置的job是否覆盖已经存在的job信息