【SpringBoot】25、SpringBoot中使用Quartz管理定时任务

【SpringBoot】25、SpringBoot中使用Quartz管理定时任务定时任务在系统中用到的地方很多,例如每晚凌晨的数据备份,每小时获取第三方平台的Token信息等等,之前我们都是在项目中规定这个定时任务什么时候启动,到时间了便会自己启动,那么我们想要停止这个定时任务的时候,就需要去改动代码,还得启停服务器,这是非常不友好的事情直至遇见Quartz,利用图形界面可视化管理定时任务,使得我们对定时任务的管理更加方便,快捷一、Quartz简介Quartz是一个开源的作业调度框架,它完全由Java写成,并设计用于J2SE和J2EE应用中。它提供了巨大的灵活性而不牺牲

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全家桶1年46,售后保障稳定

定时任务在系统中用到的地方很多,例如每晚凌晨的数据备份,每小时获取第三方平台的 Token 信息等等,之前我们都是在项目中规定这个定时任务什么时候启动,到时间了便会自己启动,那么我们想要停止这个定时任务的时候,就需要去改动代码,还得启停服务器,这是非常不友好的事情

直至遇见 Quartz,利用图形界面可视化管理定时任务,使得我们对定时任务的管理更加方便,快捷

一、Quartz 简介

Quartz是一个开源的作业调度框架,它完全由Java写成,并设计用于J2SE和J2EE应用中。它提供了巨大的灵 活性而不牺牲简单性。你能够用它来为执行一个作业而创建简单的或复杂的调度。它有很多特征,如:数据库支持,集群,插件,EJB作业预构 建,JavaMail及其它,支持cron-like表达式等等。

二、开发前戏

1、引入 maven 依赖

<!-- web支持 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Quartz 定时任务 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

Jetbrains全家桶1年46,售后保障稳定

这里引入了 web 的依赖,以及 Quartz 的依赖,其余依赖请根据需求自行引入

2、创建数据表

数据模型:
数据模型
SQL语句:

drop table if exists sys_quartz;

/*==============================================================*/
/* Table: sys_quartz */
/*==============================================================*/
create table sys_quartz
(
   id                   bigint(20) not null auto_increment comment '主键id',
   class_name           varchar(32) comment '任务类名',
   cron_expression      varchar(32) comment 'cron表达式',
   param                varchar(32) comment '参数',
   descript             varchar(11) comment '描述',
   quartz_status        varchar(255) comment '启动状态(0--启动1--停止)',
   create_time          datetime comment '创建时间',
   create_user          bigint(20) comment '创建人',
   status               tinyint(1) default 0 comment '状态(0--正常1--停用)',
   del_flag             tinyint(1) default 0 comment '删除状态(0,正常,1已删除)',
   primary key (id)
)
type = InnoDB;

alter table sys_quartz comment '定时任务信息表';

三、开发进行中

1、创建实体类

import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.zyxx.common.annotation.Dict;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

import java.io.Serializable;

/** * <p> * 定时任务信息表 * </p> * * @author lizhou * @since 2020-07-21 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("sys_quartz")
@ApiModel(value="SysQuartz对象", description="定时任务信息表")
public class SysQuartz extends Model<SysQuartz> { 
   

    @ApiModelProperty(value = "主键id")
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    @ApiModelProperty(value = "任务类名")
    @TableField("class_name")
    private String className;

    @ApiModelProperty(value = "cron表达式")
    @TableField("cron_expression")
    private String cronExpression;

    @ApiModelProperty(value = "参数")
    @TableField("param")
    private String param;

    @ApiModelProperty(value = "描述")
    @TableField("descript")
    private String descript;

    @ApiModelProperty(value = "启动状态(0--启动1--停止)")
    @TableField("quartz_status")
    private Integer quartzStatus;

    @ApiModelProperty(value = "状态(0--正常1--停用)")
    @TableField("status")
    private Integer status;

    @ApiModelProperty(value = "删除状态(0--未删除1--已删除)")
    @TableField("del_flag")
    @TableLogic
    private Integer delFlag;

    @ApiModelProperty(value = "创建者")
    @TableField("create_user")
    private Long createUser;

    @ApiModelProperty(value = "创建时间")
    @TableField("create_time")
    private String createTime;

    @Override
    protected Serializable pkVal() { 
   
        return this.id;
    }
}

2、实现定时任务的 CRUD

下面我们就要完成定时任务的 新增、修改、删除、启停 等基本操作了,由于不是很复杂,这里的代码就不贴出来了,贴几张图吧

列表页:
列表页
新增页:
新增页

四、定时任务

1、定时任务类

我们把定时任务都放在 job 包下面,一个定时任务就是一个文件,写一个测试的类 TestJob.java

import com.zyxx.common.utils.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

/** * @ClassName TestJob * 测试定时任务 * @Author Lizhou * @Date 2020-07-21 10:58:58 **/
@Slf4j
public class TestJob implements Job { 
   

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { 
   
        System.out.println("定时任务启动:" + DateUtils.getYmdHms());
    }
}

TestJob 这个类实现了 Job 接口,实现了 execute 方法,这里还可以接收参数

这个文件在 com.zyxx.sbm.job 包下面,那么在页面新增定时任务的时候,就需要填写任务类名为:com.zyxx.sbm.job.TestJob

cron 表达式的知识这里就不一一介绍了

2、页面添加定时任务

添加定时任务

那么我们的任务类名就是:com.zyxx.sbm.job.TestJob
cron 表达式:*/2 * * * * ?,表示两秒钟执行一次
参数:我们没有传入参数

3、后台添加定时任务

package com.zyxx.sbm.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zyxx.common.shiro.SingletonLoginUtils;
import com.zyxx.common.utils.DateUtils;
import com.zyxx.common.utils.LayTableResult;
import com.zyxx.common.utils.ResponseResult;
import com.zyxx.sbm.entity.SysQuartz;
import com.zyxx.sbm.mapper.SysQuartzMapper;
import com.zyxx.sbm.service.SysQuartzService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/** * <p> * 定时任务信息表 服务实现类 * </p> * * @author lizhou * @since 2020-07-21 */
@Slf4j
@Service
public class SysQuartzServiceImpl extends ServiceImpl<SysQuartzMapper, SysQuartz> implements SysQuartzService { 
   

    @Autowired
    private Scheduler scheduler;

	/** * 添加定时任务 */
    @Override
    public ResponseResult add(SysQuartz sysQuartz) { 
   
        QueryWrapper<SysQuartz> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("class_name", sysQuartz.getClassName());
        List<SysQuartz> sysQuartzList = list(queryWrapper);
        if (null != sysQuartzList && !sysQuartzList.isEmpty()) { 
   
            return ResponseResult.getInstance().error("该任务类名已经存在");
        }
        sysQuartz.setCreateTime(DateUtils.getYmdHms());
        sysQuartz.setCreateUser(SingletonLoginUtils.getUserId());
        save(sysQuartz);
        // 启动
        if (0 == sysQuartz.getQuartzStatus()) { 
   
            this.schedulerAdd(sysQuartz.getClassName().trim(), sysQuartz.getCronExpression().trim(), sysQuartz.getParam());
        }
        return ResponseResult.getInstance().success();
    }

    /** * 添加定时任务 * * @param className * @param cronExpression * @param param */
    @Override
    public void schedulerAdd(String className, String cronExpression, String param) { 
   
        try { 
   
            // 启动调度器
            scheduler.start();
            // 构建job信息
            JobDetail jobDetail = JobBuilder.newJob(getClass(className).getClass()).withIdentity(className).usingJobData("param", param).build();
            // 表达式调度构建器(即任务执行的时间)
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
            // 按新的cronExpression表达式构建一个新的trigger
            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(className).withSchedule(scheduleBuilder).build();
            scheduler.scheduleJob(jobDetail, trigger);
        } catch (SchedulerException e) { 
   
            log.error(e.getMessage());
        } catch (RuntimeException e) { 
   
            log.error(e.getMessage());
        } catch (Exception e) { 
   
            log.error(e.getMessage());
        }
    }

    /** * 删除定时任务 * * @param className */
    @Override
    public void schedulerDelete(String className) { 
   
        try { 
   
            scheduler.pauseTrigger(TriggerKey.triggerKey(className));
            scheduler.unscheduleJob(TriggerKey.triggerKey(className));
            scheduler.deleteJob(JobKey.jobKey(className));
        } catch (Exception e) { 
   
            log.error(e.getMessage(), e);
        }
    }

    private static Job getClass(String className) throws Exception { 
   
        Class<?> class1 = Class.forName(className);
        return (Job) class1.newInstance();
    }
}

需要注入 Scheduler 对象,使用该对象开启或停止定时任务

在启动定时任务之前,我们应先删除该任务类名开启的定时任务,防止该任务类名已经添加过了

// 删除定时任务
schedulerDelete(sysQuartz.getClassName().trim());
// 添加定时任务
schedulerAdd(sysQuartz.getClassName().trim(), sysQuartz.getCronExpression().trim(), sysQuartz.getParam());

添加定时任务,传入任务类名,cron 表达式,参数

停止定时任务,只需要:

scheduler.pauseJob(JobKey.jobKey(sysQuartz.getClassName().trim()));

根据任务类名,停止定时任务即可

五、开发测试

启动项目,在管理界面,开启定时任务,即可在控制台看到打印的信息
打印信息
表示我们的定时任务已经启动成功了

六、优化建议

当我们添加了定时任务并启动后,重新启动项目的时候,定时任务却不会自动启动,这时候,我们就需要在项目启动的时候做一些事情了,也就是系统启动任务

不清楚的同学可以复习一下之前我的博客【SpringBoot】十九、SpringBoot中实现启动任务

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zyxx.sbm.entity.SysQuartz;
import com.zyxx.sbm.service.SysQuartzService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.List;

/** * @ClassName SystemStartTask * 项目启动任务--启动定时任务 * @Author Lizhou * @Date 2020-07-21 12:56:56 **/
@Component
@Order(100)
public class SystemQuartzStartTask implements CommandLineRunner { 
   

    @Autowired
    private SysQuartzService sysQuartzService;

    @Override
    public void run(String... args) throws Exception { 
   
        // 查询启动的定时任务
        QueryWrapper<SysQuartz> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("status", 0);
        queryWrapper.eq("quartz_status", 0);
        List<SysQuartz> list = sysQuartzService.list(queryWrapper);
        if (null != list && !list.isEmpty()) { 
   
            for (SysQuartz item : list) { 
   
                // 删除定时任务
                sysQuartzService.schedulerDelete(item.getClassName().trim());
                // 添加定时任务
                sysQuartzService.schedulerAdd(item.getClassName().trim(), item.getCronExpression().trim(), item.getParam());
            }
        }
    }
}

从数据库查询出启动的定时任务,并将他们添加到定时任务启动中,这样项目一启动时,就会自动启动我们定义的定时任务了

最后

任务类名的正则表达式

/^[a-zA-Z]+(\.([a-zA-Z])+)+$/

cron 表达式的验证使用正则太麻烦,可以使用 Quartz 自带验证方法

CronExpression.isValidExpression(cron)

SpringBoot 中使用 Quartz 管理定时任务的学习就到这儿了,其实也并不难理解,相比于之前用的定时任务是不是好很多了呢,别忘了最后加上系统启动任务哦

如您在阅读中发现不足,欢迎留言!!!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/200849.html原文链接:https://javaforall.net

(0)
上一篇 2025年8月20日 下午12:15
下一篇 2025年8月20日 下午12:43


相关推荐

  • window 下蓝牙开发[通俗易懂]

    window 下蓝牙开发[通俗易懂]window蓝牙开发资料真的是比较少,而且功能比较单一。使用window自带的蓝牙重连接真的是太慢了。找不到好的解决方法,很让人头痛呀!下面提供的这点代码,也只是一个简单的功能,有这方面研究的朋友,希望能多多交流,多多指教ULONGLONGg_ulRemoteAddr=0;WCHARstrGUID[]=L”00001101-0000-1000-8000-00805f9b34fb”;

    2025年6月29日
    4
  • 【接口测试实战(三)】接口测试用例的编写

    【接口测试实战(三)】接口测试用例的编写文章目录 1 接口测试发现的典型问题 2 接口测试用例设计 2 1 针对输入设计常见参数类型有 2 1 1 数值型常见问题和风险 设计举例 2 1 2 字符串型常见问题和风险 设计举例 2 1 3 数组或链表类型常见问题和风险 设计举例 2 2 针对逻辑设计 2 2 1 约束条件分析常见的约束条件 常见的问题和风险 设计举例 2 2 2 操作对象分析常见的问题和风险 设计举例 2 2 3 状态转换分析常见的问题和风险 设计举例 2 2 4 时序分析常见的问题和风险 设计举例 2 3 针对输出设计 2 3 1 针对输出

    2026年3月16日
    1
  • Darknet-53_darknet_track

    Darknet-53_darknet_track今天想下载这个文件,百度一搜,好多博主要收费才能下载,我就奇怪了,这玩意又不是他自己脑力活动创造的代码,收啥费啊,现在免费分享这个链接:链接:https://pan.baidu.com/s/17yywRWP-IaGXT6es1u5_-A提取码:fggd各位看官,拿走的时候顺便点个赞吧。20204.24…

    2025年6月20日
    63
  • 宝塔安装mysql失败_手机如何卸载宝塔防火墙

    宝塔安装mysql失败_手机如何卸载宝塔防火墙先用宝塔自带卸载宝塔软件环境默认是不卸载得mysql先停止服务,再删除服务apache先停止服务,再删除服务然后安装新的宝塔即可

    2025年9月22日
    5
  • 数仓数据分层(ODS DWD DWS ADS)换个角度看

    数仓数据分层(ODS DWD DWS ADS)换个角度看数仓数据分层简介1.背景数仓是什么,其实就是存储数据,体现历史变化的一个数据仓库.因为互联网时代到来,基于数据量的大小,分为了传统数仓和现代数仓.传统数仓,使用传统的关系型数据库进行数据存储,因为关系型数据库本身可以使用SQL以及函数等做数据分析.所以把数据存储和数据分析功能集合为一体,加上一个可视化界面,就能从数据存储,数据分析,数据展示完整方案.到了互联网时代,由于上网用户剧增,特别是移动互联网时代,海量的网络设备,导致了海量的数据产生,企业需要也希望从这些海量数据中挖掘有效信息,如行为

    2022年6月26日
    77
  • Tcp是什么?_跟你说完了

    Tcp是什么?_跟你说完了之前受到Wireshark——从此我就喜欢上了它,就像是学武之人得到了一把称手好剑的启发,带着回顾、深入TCP的目标,回顾了《TCP-IP协议卷1》《图解TCP/IP协议》,受益匪浅。写这篇文章,希望自己能对TCP形成一个系统性的知识沉淀,也希望能给初学者一个基本概念的认识,读完本文再深入书籍,应该也是不错滴。学习路径:1、阅读《TCP-IP协议卷1》的TCP章节(相关知识非常全面,各种算法…

    2025年9月17日
    10

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号