玩转 Spring Boot 集成篇(定时任务框架Quartz)

发布时间 - 2025-07-14 00:00:00    点击率:

在日常项目研发中,定时任务可谓是必不可少的一环,关于 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运行复制
    org.springframework.boot    spring-boot-starter-quartz

1. 内存方式存储任务信息

1.1 定义任务类

可以通过实现 Job 接口来定义任务,也可以通过继承 QuartzJobBean 这个抽象类来定义任务,其实 QuartzJobBean 本身也实现了 Job 接口,其本质都是实现 Job 接口来定义任务。

代码语言:javascript代码运行次数:0运行复制
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("幼年是盼盼,青年是晶晶,中年是冰墩墩,生活见好逐渐发福");    }}

1.2 定义任务描述及任务触发规则

定义每隔 5 秒执行一次任务,代码如下。

代码语言:javascript代码运行次数:0运行复制
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();    }}

1.3 程序入口 main 函数

代码语言:javascript代码运行次数:0运行复制
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);    }}

1.4 运行验证

通过控制台输出能够看到每 5 秒输出一次信息,达到预期效果。

通过控制台标注 1 的部分能够看到默认采用的是 RAMJobStore,就是将任务相关信息保存在内存里,应用重启后,定时任务信息将会丢失。

2. 数据库方式存储任务信息

2.1 引入依赖

在 pom.xml 文件中加入如下依赖信息。

代码语言:javascript代码运行次数:0运行复制
    com.mchange    c3p0    0.9.5.4    mysql    mysql-connector-java

2.2 添加 Quartz 配置信息

在 application.properties 文件中加入 Quartz 相关配置。

代码语言:javascript代码运行次数:0运行复制
# 将 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

2.3 初始化 Quartz 数据表信息

下载 Quartz 发布包,下载完成后,解压缩进入 quartz-2.2.3/docs/dbTables 目录,找到匹配数据库的 SQL 文件。

代码语言:javascript代码运行次数:0运行复制
下载地址:https://www.quartz-scheduler.org/downloads/files/quartz-2.2.3-distribution.tar.gz

为了方便演示,单独创建 quartz_jobs 数据库。

代码语言:javascript代码运行次数:0运行复制
create database quartz_jobs default charset utf8;use quartz_jobs;

然后在数据库中执行 tables-mysql.sql 初始化脚本,脚本内容如下(来源于官网压缩包),先别细究每个表的意义,用起来才是硬道理。

代码语言:javascript代码运行次数:0运行复制
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;

2.4 运行验证

代码无需任何变动,直接运行 main 函数,控制台输出如下:

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

至此,Spring Boot 集成 Quartz 的两种存储任务方式就完事儿了,其实很简单,稍显复杂的多是 Quartz 相关的持久化依赖的 SQL 及配置信息。

但是,在某些业务场景下要求任务必须高可用、可扩展,那么单台服务器不能满足业务需求,这时就需要开启 Quartz 分布式定时任务啦。

3. 分布式任务支持

3.1 开启集群配置

在 application.properties 文件中,加入 Quartz 集群的配置信息。

代码语言:javascript代码运行次数:0运行复制
# 开启集群,多个 Quartz 实例使用同一组数据库表spring.quartz.properties.org.quartz.jobStore.isClustered=true

注意 Quartz 使用同一组数据库表作集群时,只需要配置相同的 instanceName 实例名称就可以,例如本次都用 SC_Scheduler。

代码语言:javascript代码运行次数:0运行复制
 spring.quartz.properties.org.quartz.scheduler.instanceName=SC_Scheduler

3.2 运行验证

为了方便启动多实例验证,把服务启动的端口进行随机生成,application.properties 文件加入配置如下。

代码语言:javascript代码运行次数:0运行复制
server.port=${random.int[10000,19999]}

直接运行 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 任务的动态管理。

历史系列文章:


# mysql  # apache  # ai  # c#  # red  # JavaScript  # sql  # spring  # spring boot  # 分布式  # 封装  # xml  # 继承  # 接口  # 线程  # 多线程  # idea  # 数据库  # 可以通过  # 都是  # 重启  # 执行时间  # 稍显  # 单机版  # 每隔  # 盼盼  # 的是  # 也有 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 北京网站制作的公司有哪些,北京白云观官方网站?  如何将凡科建站内容保存为本地文件?  Laravel如何使用模型观察者?(Observer代码示例)  ,南京靠谱的征婚网站?  Laravel广播系统如何实现实时通信_Laravel Reverb与WebSockets实战教程  Laravel如何优化应用性能?(缓存和优化命令)  Win11摄像头无法使用怎么办_Win11相机隐私权限开启教程【详解】  谷歌浏览器如何更改浏览器主题 Google Chrome主题设置教程  公司门户网站制作公司有哪些,怎样使用wordpress制作一个企业网站?  企业在线网站设计制作流程,想建设一个属于自己的企业网站,该如何去做?  php中::能调用final静态方法吗_final修饰静态方法调用规则【解答】  中国移动官方网站首页入口 中国移动官网网页登录  Laravel如何使用软删除(Soft Deletes)功能_Eloquent软删除与数据恢复方法  Laravel如何实现一对一模型关联?(Eloquent示例)  Laravel如何使用Scope本地作用域_Laravel模型常用查询逻辑封装技巧【手册】  Laravel Octane如何提升性能_使用Laravel Octane加速你的应用  如何登录建站主机?访问步骤全解析  Laravel如何操作JSON类型的数据库字段?(Eloquent示例)  如何在建站宝盒中设置产品搜索功能?  如何在 Telegram Web View(iOS)中防止键盘遮挡底部输入框  用yum安装MySQLdb模块的步骤方法  高配服务器限时抢购:企业级配置与回收服务一站式优惠方案  如何在阿里云部署织梦网站?  如何续费美橙建站之星域名及服务?  如何用PHP工具快速搭建高效网站?  Laravel如何处理跨站请求伪造(CSRF)保护_Laravel表单安全机制与令牌校验  佛山企业网站制作公司有哪些,沟通100网上服务官网?  Laravel如何记录日志_Laravel Logging系统配置与自定义日志通道  如何快速使用云服务器搭建个人网站?  百度浏览器如何管理插件 百度浏览器插件管理方法  Laravel如何使用Blade组件和插槽?(Component代码示例)  黑客如何通过漏洞一步步攻陷网站服务器?  网站制作报价单模板图片,小松挖机官方网站报价?  怎么制作网站设计模板图片,有电商商品详情页面的免费模板素材网站推荐吗?  如何在万网自助建站平台快速创建网站?  如何正确选择百度移动适配建站域名?  怎么用AI帮你设计一套个性化的手机App图标?  Laravel队列由Redis驱动怎么配置_Laravel Redis队列使用教程  厦门模型网站设计制作公司,厦门航空飞机模型掉色怎么办?  广州网站制作公司哪家好一点,广州欧莱雅百库网络科技有限公司官网?  html如何与html链接_实现多个HTML页面互相链接【互相】  Laravel表单请求验证类怎么用_Laravel Form Request分离验证逻辑教程  Laravel辅助函数有哪些_Laravel Helpers常用助手函数大全  网站广告牌制作方法,街上的广告牌,横幅,用PS还是其他软件做的?  Laravel怎么返回JSON格式数据_Laravel API资源Response响应格式化【技巧】  网站视频制作书签怎么做,ie浏览器怎么将网站固定在书签工具栏?  如何用美橙互联一键搭建多站合一网站?  ChatGPT常用指令模板大全 新手快速上手的万能Prompt合集  如何快速生成凡客建站的专业级图册?  高端云建站费用究竟需要多少预算?