在日常项目研发中,定时任务可谓是必不可少的一环,关于 spring boot 如何实现静态定时任务、动态定时任务以及如何开启多线程跑任务,均已在上篇分享过,不再赘述。
虽然 Spring Boot 内置注解方式实现的定时任务,在一定程度上也能解决一定的业务场景问题,但是若做更复杂的动作,例如启停任务、删除任务等等操作,实现起来则稍显复杂,此时便可以通过集成开源任务框架来实现。
常见的定时任务框架有 Quartz、elastic-job、xxl-job等等,本次主要介绍 Spring Boot 集成定时任务第 3 部分:Spirng Boot 集成 Quartz 定时任务框架。

Quartz 存储方式有两种:MEMORY 和 JDBC。默认是内存形式维护任务信息,意味着服务重启了任务就从头再来,就像喝酒断片了一样;而 JDBC 形式就是能够把任务信息持久化到数据库,虽然服务重启了,依然还能接着来。
Quartz 提供了单机版和集群版,默认就是单机版,接下来逐一分享一波。
Spring Boot 集成 Quartz 的方式也很简单,首先引入封装好的 Quartz 依赖。
代码语言:javascript代码运行次数:0运行复制<code class="javascript"><!-- 引入 Quartz 依赖--><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId></dependency></code>
1. 内存方式存储任务信息
1.1 定义任务类
可以通过实现 Job 接口来定义任务,也可以通过继承 QuartzJobBean 这个抽象类来定义任务,其实 QuartzJobBean 本身也实现了 Job 接口,其本质都是实现 Job 接口来定义任务。
代码语言:javascript代码运行次数:0运行复制<code class="javascript">import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.quartz.JobExecutionContext;import org.quartz.JobExecutionException;import org.springframework.scheduling.quartz.QuartzJobBean;/** * 定义任务 */public class DongAoJob extends QuartzJobBean { private static final Log logger = LogFactory.getLog(DongAoJob.class); @Override protected void executeInternal(JobExecutionContext context) throws JobExecutionException { logger.info("幼年是盼盼,青年是晶晶,中年是冰墩墩,生活见好逐渐发福"); }}</code>1.2 定义任务描述及任务触发规则
定义每隔 5 秒执行一次任务,代码如下。
代码语言:javascript代码运行次数:0运行复制<code class="javascript">import com.example.demo.quartz.task.DongAoJob;import org.quartz.*;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;/** * 定义任务描述和具体的执行时间 */@Configurationpublic class QuartzConfig { @Bean public JobDetail jobDetail() { //指定任务描述具体的实现类 return JobBuilder.newJob(DongAoJob.class) // 指定任务的名称 .withIdentity("dongAoJob") // 任务描述 .withDescription("任务描述:用于输出冬奥欢迎语") // 每次任务执行后进行存储 .storeDurably() .build(); } @Bean public Trigger trigger() { //创建触发器 return TriggerBuilder.newTrigger() // 绑定工作任务 .forJob(jobDetail()) // 每隔 5 秒执行一次 job .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5)) .build(); }}</code>1.3 程序入口 main 函数
代码语言:javascript代码运行次数:0运行复制<code class="javascript">import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class DemoJobApplication { public static void main(String[] args) { SpringApplication.run(DemoJobApplication.class, args); }}</code>1.4 运行验证

通过控制台输出能够看到每 5 秒输出一次信息,达到预期效果。
通过控制台标注 1 的部分能够看到默认采用的是 RAMJobStore,就是将任务相关信息保存在内存里,应用重启后,定时任务信息将会丢失。
2. 数据库方式存储任务信息
2.1 引入依赖
在 pom.xml 文件中加入如下依赖信息。
代码语言:javascript代码运行次数:0运行复制<code class="javascript"><dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.4</version></dependency><!-- MySQL 驱动 --><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId></dependency></code>
2.2 添加 Quartz 配置信息
在 application.properties 文件中加入 Quartz 相关配置。
代码语言:javascript代码运行次数:0运行复制<code class="javascript"># 将 Quartz 持久化方式修改为 jdbcspring.quartz.job-store-type=jdbc# 实例名称(默认为quartzScheduler)spring.quartz.properties.org.quartz.scheduler.instanceName=SC_Scheduler# 实例节点 ID 自动生成spring.quartz.properties.org.quartz.scheduler.instanceId=AUTO# 修改存储内容使用的类spring.quartz.properties.org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX# 数据源信息spring.quartz.properties.org.quartz.jobStore.dataSource=quartz_jobsspring.quartz.properties.org.quartz.dataSource.quartz_jobs.driver=com.mysql.cj.jdbc.Driverspring.quartz.properties.org.quartz.dataSource.quartz_jobs.URL=jdbc:mysql://127.0.0.1:3306/quartz_jobs?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8spring.quartz.properties.org.quartz.dataSource.quartz_jobs.user=rootspring.quartz.properties.org.quartz.dataSource.quartz_jobs.password=123456</code>
2.3 初始化 Quartz 数据表信息
下载 Quartz 发布包,下载完成后,解压缩进入 quartz-2.2.3/docs/dbTables 目录,找到匹配数据库的 SQL 文件。
代码语言:javascript代码运行次数:0运行复制<code class="javascript">下载地址:https://www.quartz-scheduler.org/downloads/files/quartz-2.2.3-distribution.tar.gz</code>
为了方便演示,单独创建 quartz_jobs 数据库。
代码语言:javascript代码运行次数:0运行复制<code class="javascript">create database quartz_jobs default charset utf8;use quartz_jobs;</code>
然后在数据库中执行 tables-mysql.sql 初始化脚本,脚本内容如下(来源于官网压缩包),先别细究每个表的意义,用起来才是硬道理。
代码语言:javascript代码运行次数:0运行复制<code class="javascript">DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;DROP TABLE IF EXISTS QRTZ_LOCKS;DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;DROP TABLE IF EXISTS QRTZ_TRIGGERS;DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;DROP TABLE IF EXISTS QRTZ_CALENDARS;CREATE TABLE QRTZ_JOB_DETAILS ( SCHED_NAME VARCHAR(120) NOT NULL, JOB_NAME VARCHAR(200) NOT NULL, JOB_GROUP VARCHAR(200) NOT NULL, DESCRIPTION VARCHAR(250) NULL, JOB_CLASS_NAME VARCHAR(250) NOT NULL, IS_DURABLE VARCHAR(1) NOT NULL, IS_NONCONCURRENT VARCHAR(1) NOT NULL, IS_UPDATE_DATA VARCHAR(1) NOT NULL, REQUESTS_RECOVERY VARCHAR(1) NOT NULL, JOB_DATA BLOB NULL, PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP));CREATE TABLE QRTZ_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, JOB_NAME VARCHAR(200) NOT NULL, JOB_GROUP VARCHAR(200) NOT NULL, DESCRIPTION VARCHAR(250) NULL, NEXT_FIRE_TIME BIGINT(13) NULL, PREV_FIRE_TIME BIGINT(13) NULL, PRIORITY INTEGER NULL, TRIGGER_STATE VARCHAR(16) NOT NULL, TRIGGER_TYPE VARCHAR(8) NOT NULL, START_TIME BIGINT(13) NOT NULL, END_TIME BIGINT(13) NULL, CALENDAR_NAME VARCHAR(200) NULL, MISFIRE_INSTR SMALLINT(2) NULL, JOB_DATA BLOB NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP) REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP));CREATE TABLE QRTZ_SIMPLE_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, REPEAT_COUNT BIGINT(7) NOT NULL, REPEAT_INTERVAL BIGINT(12) NOT NULL, TIMES_TRIGGERED BIGINT(10) NOT NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP));CREATE TABLE QRTZ_CRON_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, CRON_EXPRESSION VARCHAR(200) NOT NULL, TIME_ZONE_ID VARCHAR(80), PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP));CREATE TABLE QRTZ_SIMPROP_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, STR_PROP_1 VARCHAR(512) NULL, STR_PROP_2 VARCHAR(512) NULL, STR_PROP_3 VARCHAR(512) NULL, INT_PROP_1 INT NULL, INT_PROP_2 INT NULL, LONG_PROP_1 BIGINT NULL, LONG_PROP_2 BIGINT NULL, DEC_PROP_1 NUMERIC(13,4) NULL, DEC_PROP_2 NUMERIC(13,4) NULL, BOOL_PROP_1 VARCHAR(1) NULL, BOOL_PROP_2 VARCHAR(1) NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP));CREATE TABLE QRTZ_BLOB_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, BLOB_DATA BLOB NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP));CREATE TABLE QRTZ_CALENDARS ( SCHED_NAME VARCHAR(120) NOT NULL, CALENDAR_NAME VARCHAR(200) NOT NULL, CALENDAR BLOB NOT NULL, PRIMARY KEY (SCHED_NAME,CALENDAR_NAME));CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP));CREATE TABLE QRTZ_FIRED_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, ENTRY_ID VARCHAR(95) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, INSTANCE_NAME VARCHAR(200) NOT NULL, FIRED_TIME BIGINT(13) NOT NULL, SCHED_TIME BIGINT(13) NOT NULL, PRIORITY INTEGER NOT NULL, STATE VARCHAR(16) NOT NULL, JOB_NAME VARCHAR(200) NULL, JOB_GROUP VARCHAR(200) NULL, IS_NONCONCURRENT VARCHAR(1) NULL, REQUESTS_RECOVERY VARCHAR(1) NULL, PRIMARY KEY (SCHED_NAME,ENTRY_ID));CREATE TABLE QRTZ_SCHEDULER_STATE ( SCHED_NAME VARCHAR(120) NOT NULL, INSTANCE_NAME VARCHAR(200) NOT NULL, LAST_CHECKIN_TIME BIGINT(13) NOT NULL, CHECKIN_INTERVAL BIGINT(13) NOT NULL, PRIMARY KEY (SCHED_NAME,INSTANCE_NAME));CREATE TABLE QRTZ_LOCKS ( SCHED_NAME VARCHAR(120) NOT NULL, LOCK_NAME VARCHAR(40) NOT NULL, PRIMARY KEY (SCHED_NAME,LOCK_NAME));commit;</code>
2.4 运行验证
代码无需任何变动,直接运行 main 函数,控制台输出如下:

通过控制台输出发现任务正常跑起来,而且任务存储的方式变为了 jdbcjobstore.JobStoreTx,此时数据库表中也有任务信息插入了。

至此,Spring Boot 集成 Quartz 的两种存储任务方式就完事儿了,其实很简单,稍显复杂的多是 Quartz 相关的持久化依赖的 SQL 及配置信息。
但是,在某些业务场景下要求任务必须高可用、可扩展,那么单台服务器不能满足业务需求,这时就需要开启 Quartz 分布式定时任务啦。
3. 分布式任务支持
3.1 开启集群配置
在 application.properties 文件中,加入 Quartz 集群的配置信息。
代码语言:javascript代码运行次数:0运行复制<code class="javascript"># 开启集群,多个 Quartz 实例使用同一组数据库表spring.quartz.properties.org.quartz.jobStore.isClustered=true</code>
注意 Quartz 使用同一组数据库表作集群时,只需要配置相同的 instanceName 实例名称就可以,例如本次都用 SC_Scheduler。
代码语言:javascript代码运行次数:0运行复制<code class="javascript"> spring.quartz.properties.org.quartz.scheduler.instanceName=SC_Scheduler</code>
3.2 运行验证
为了方便启动多实例验证,把服务启动的端口进行随机生成,application.properties 文件加入配置如下。
代码语言:javascript代码运行次数:0运行复制<code class="javascript">server.port=${random.int[10000,19999]}</code>直接运行 main 函数,启动实例 1;再次运行 main 函数,启动实例2(如果 IDEA 不让重复运行,那就重新复制一个启动类,再运行)。

通过红色圈住部分 is clustered,显然已是开启了集群。若关闭其中一个正在跑任务的节点,观察另一个节点是否会自动检测继续执行任务呢?
停掉节点 1:

节点 2 继续接着执行任务:

至此,Spring Boot 集成 Quartz 可以接近尾声了。
不过稍显不甘心呢,因为回头看代码,会发现 QuartzConfig 中的 job 和 trigger 都是硬编码方式完成的。

这样肯定无法管理任务的状态,无法做到扩展,更达不到 Spring Boot 的思想让程序员更专注业务开发,所以仍有改进的空间,仍存很多疑问。
疑问:是否可以通过 API 动态创建任务呢?
疑问:是否可以通过 API 编辑任务的执行时间呢?
疑问:是否可以通过 API 暂停/恢复任务呢?
疑问:是否可以通过 API 删除任务呢?
疑问:是否可以 ... ...
此时,可以这么回答:of course.Im very sure. 老鼠拉木锨——大头在后面呢,不过限于自定义代码较多,单独放一代码篇去分享。
4. 例行回顾
本文是 Spring Boot 项目集成 Quartz 定时任务框架讲解,主要分享了如下部分:
默认内存方式存储任务信息;数据库方式任务信息;分布式任务支持玩转 Spring Boot 集成 Quartz 定时任务就写到这里,下次一起编码实现 Quartz 任务的动态管理。
历史系列文章:
以上就是玩转 Spring Boot 集成篇(定时任务框架Quartz)的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号