Quartz 简单使用

Java SpringBoot 中,动态执行 bean 对象中的方法

源代码地址 => https://gitee.com/VipSoft/VipBoot/tree/develop/vipsoft-quartz

工作原理解读

只要配置好 DataSource Quartz 会自动进行表的数据操作,

添加 Quartz Job 任务

保存 QRTZ_JOB_DETAILS、QRTZ_TRIGGERS => QRTZ_CRON_TRIGGERS

public void addJob(QuartzJob job) throws SchedulerException {
....
JobDetail jobDetail = JobBuilder.newJob(jobClass)
.withIdentity(jobKey)
.build();
// 放入参数,运行时的方法可以获取
jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job);
//该行代码执行后,会将定时任务插入 QRTZ_JOB_DETAILS 等相关表
scheduler.scheduleJob(jobDetail, trigger);
....
}
//org.quartz.impl.jdbcjobstore.JobStoreSupport
public void storeJobAndTrigger(final JobDetail newJob, final OperableTrigger newTrigger) throws JobPersistenceException {
this.executeInLock(this.isLockOnInsert() ? "TRIGGER_ACCESS" : null, new JobStoreSupport.VoidTransactionCallback() {
public void executeVoid(Connection conn) throws JobPersistenceException {
JobStoreSupport.this.storeJob(conn, newJob, false); //数据保存 QRTZ_JOB_DETAILS 表
JobStoreSupport.this.storeTrigger(conn, newTrigger, newJob, false, "WAITING", false, false); //数据保存 QRTZ_TRIGGERS 表
}
});
} public int insertTrigger(...){
INSERT_TRIGGER
insertExtendedTriggerProperties => INSERT_CRON_TRIGGER OR INSERT_BLOB_TRIGGER }

详见:org.quartz.impl.jdbcjobstore.StdJDBCDelegate

将 job.getJobDataMap(),对像序列化后,存入 JOB_DETAILS.JOB_DATA字段,可以是一个对像,以执行定时任务时,会把该字段反序列化,根据前期设定的内容进行业务处理

获取 Quartz Job 任务

执行计划任务时,获取 Job Detail

QuartzSchedulerThread.run()
=> qsRsrcs.getJobStore().acquireNextTriggers()
=> txCallback.execute(conn)
=> JobStoreSupport.acquireNextTriggers()
=> JobStoreSupport.retrieveJob()
=> StdJDBCDelegate.selectJobDetail()

删除 Quartz Job 任务

/**
* <p>
* Delete the base trigger data for a trigger.
* </p>
*
* @param conn
* the DB Connection
* @return the number of rows deleted
*/
public int deleteTrigger(Connection conn, TriggerKey triggerKey) throws SQLException {
PreparedStatement ps = null; deleteTriggerExtension(conn, triggerKey); try {
ps = conn.prepareStatement(rtp(DELETE_TRIGGER));
ps.setString(1, triggerKey.getName());
ps.setString(2, triggerKey.getGroup()); return ps.executeUpdate();
} finally {
closeStatement(ps);
}
}

清除数据

/**
* 清任务顺序
*/
public void clearData(Connection conn)
throws SQLException { PreparedStatement ps = null; try {
ps = conn.prepareStatement(rtp(DELETE_ALL_SIMPLE_TRIGGERS));
ps.executeUpdate();
ps.close();
ps = conn.prepareStatement(rtp(DELETE_ALL_SIMPROP_TRIGGERS));
ps.executeUpdate();
ps.close();
ps = conn.prepareStatement(rtp(DELETE_ALL_CRON_TRIGGERS));
ps.executeUpdate();
ps.close();
ps = conn.prepareStatement(rtp(DELETE_ALL_BLOB_TRIGGERS));
ps.executeUpdate();
ps.close();
ps = conn.prepareStatement(rtp(DELETE_ALL_TRIGGERS));
ps.executeUpdate();
ps.close();
ps = conn.prepareStatement(rtp(DELETE_ALL_JOB_DETAILS));
ps.executeUpdate();
ps.close();
ps = conn.prepareStatement(rtp(DELETE_ALL_CALENDARS));
ps.executeUpdate();
ps.close();
ps = conn.prepareStatement(rtp(DELETE_ALL_PAUSED_TRIGGER_GRPS));
ps.executeUpdate();
} finally {
closeStatement(ps);
}
}

Demo 代码

MySQL 脚本

https://github.com/quartz-scheduler/quartz/blob/v2.3.2/quartz-core/src/main/resources/org/quartz/impl/jdbcjobstore/tables_mysql.sql

清除数据

DELETE FROM qrtz_simple_triggers ;
DELETE FROM qrtz_simprop_triggers ;
DELETE FROM qrtz_cron_triggers ;
DELETE FROM qrtz_blob_triggers ;
DELETE FROM qrtz_triggers ;
DELETE FROM qrtz_job_details ;
DELETE FROM qrtz_calendars ;
DELETE FROM qrtz_paused_trigger_grps ;
DELETE FROM qrtz_scheduler_state ;
DELETE FROM qrtz_locks ;
DELETE FROM qrtz_fired_triggers

Pom.xml

如果SpringBoot版本是2.0.0以后的,则在spring-boot-starter中已经包含了quart的依赖,则可以直接使用spring-boot-starter-quartz依赖:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<!--Quartz 集成需要和数据库交互-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.0.8</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.20</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<!--hutool 工具类-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.3.7</version>
</dependency>



QuartzJob 参考上图,建立实体

点击查看代码
package com.vipsoft.web.entity;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import java.io.Serializable; /**
* 定时任务调度
*/
public class QuartzJob implements Serializable { private static final long serialVersionUID = -6798153039624729495L; /**
* 任务序号
*/
private int jobId; /**
* 任务名称
*/
@NotBlank(message = "任务名称不能为空")
@Size(max = 10, message = "任务名称不能超过10个字符")
private String jobName; /**
* 任务组名
*/
@NotBlank(message = "任务组名不能为空")
@Size(max = 10, message = "任务组名不能超过10个字符")
private String jobGroup; /**
* 调用目标字符串
*/
private String invokeTarget; /**
* 执行表达式
*/
private String cronExpression; /**
* cron计划策略 0=默认,1=立即触发执行,2=触发一次执行,3=不触发立即执行
*/
private String misfirePolicy = "0"; /**
* 并发执行 0=允许,1=禁止
*/
private String concurrent; /**
* 描述 -- 任务说明
*/
private String description; /**
* 任务状态(0正常 1暂停)
*/
private String status; public int getJobId() {
return jobId;
} public void setJobId(int jobId) {
this.jobId = jobId;
} public String getJobName() {
return jobName;
} public void setJobName(String jobName) {
this.jobName = jobName;
} public String getJobGroup() {
return jobGroup;
} public void setJobGroup(String jobGroup) {
this.jobGroup = jobGroup;
} public String getInvokeTarget() {
return invokeTarget;
} public void setInvokeTarget(String invokeTarget) {
this.invokeTarget = invokeTarget;
} public String getCronExpression() {
return cronExpression;
} public void setCronExpression(String cronExpression) {
this.cronExpression = cronExpression;
} public String getMisfirePolicy() {
return misfirePolicy;
} public void setMisfirePolicy(String misfirePolicy) {
this.misfirePolicy = misfirePolicy;
} public String getConcurrent() {
return concurrent;
} public void setConcurrent(String concurrent) {
this.concurrent = concurrent;
} public String getStatus() {
return status;
} public void setStatus(String status) {
this.status = status;
} public String getDescription() {
return description;
} public void setDescription(String description) {
this.description = description;
}
}

核心代码:QuartzJobServiceImpl

点击查看代码
package com.vipsoft.web.service.impl;

import cn.hutool.core.util.StrUtil;
import com.vipsoft.web.config.ScheduleConstants;
import com.vipsoft.web.entity.QuartzJob;
import com.vipsoft.web.exception.CustomException;
import com.vipsoft.web.job.CommonJob; import com.vipsoft.web.service.IQuartzJobService;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import java.util.ArrayList;
import java.util.List;
import java.util.Set; @Service
public class QuartzJobServiceImpl implements IQuartzJobService { @Autowired
Scheduler scheduler; /**
* 新增任务
*
* @param job 调度信息
* @return 结果
*/
@Override
public void clearAll(QuartzJob job) throws SchedulerException {
scheduler.clear();
}
/**
* 新增任务
*
* @param job 调度信息
* @return 结果
*/
@Override
public void addJob(QuartzJob job) throws SchedulerException {
if (StrUtil.isEmpty(job.getStatus())) {
// 如果没值,设置暂停
job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
}
Class<? extends Job> jobClass = CommonJob.class;
// 构建job信息
int jobId = job.getJobId();
String jobName = job.getJobName();
String jobGroup = job.getJobGroup();
JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup); JobDetail jobDetail = JobBuilder.newJob(jobClass)
.withIdentity(jobKey)
.build();
// 放入参数,运行时的方法可以获取
jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job); // 表达式调度构建器
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
cronScheduleBuilder = handleCronScheduleMisfirePolicy(job.getMisfirePolicy(), cronScheduleBuilder); // 按新的cronExpression表达式构建一个新的trigger
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity(triggerKey)
.withSchedule(cronScheduleBuilder)
.build(); // 判断是否存在
if (scheduler.checkExists(jobKey)) {
// 防止创建时存在数据问题 先移除,然后在执行创建操作
scheduler.deleteJob(jobKey);
} scheduler.scheduleJob(jobDetail, trigger); // 暂停任务
if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue())) {
scheduler.pauseJob(jobKey);
}
} /**
* 设置定时任务策略
*/
public static CronScheduleBuilder handleCronScheduleMisfirePolicy(String misfirePolicy, CronScheduleBuilder cb) {
switch (misfirePolicy) {
case ScheduleConstants.MISFIRE_DEFAULT:
return cb;
case ScheduleConstants.MISFIRE_IGNORE_MISFIRES:
return cb.withMisfireHandlingInstructionIgnoreMisfires();
case ScheduleConstants.MISFIRE_FIRE_AND_PROCEED:
return cb.withMisfireHandlingInstructionFireAndProceed();
case ScheduleConstants.MISFIRE_DO_NOTHING:
return cb.withMisfireHandlingInstructionDoNothing();
default:
throw new CustomException(60001, "策略配置异常 " + misfirePolicy);
}
} /**
* 更新任务
*
* @param job 调度信息
* @return 结果
*/
@Override
public void updateJob(QuartzJob job) throws SchedulerException {
// 判断是否存在
JobKey jobKey = JobKey.jobKey(job.getJobName(), job.getJobGroup());
if (scheduler.checkExists(jobKey)) {
// 防止创建时存在数据问题 先移除,然后在执行创建操作
scheduler.deleteJob(jobKey);
}
addJob(job);
} /**
* 删除任务
*
* @param job
* @Date 2016年1月16日
* @since 2.0.0
*/
@Override
public void deleteJob(QuartzJob job) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(job.getJobName(), job.getJobGroup());
if (this.scheduler.checkExists(jobKey)) {
this.scheduler.deleteJob(jobKey);
}
} /**
* 立即运行任务
*
* @param job 调度信息
* @return 结果
*/
@Override
public void run(QuartzJob job) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(job.getJobName(), job.getJobGroup());
scheduler.triggerJob(jobKey);
} /**
* 暂停任务
*
* @param job 调度信息
* @return 结果
*/
@Override
public void pauseJob(QuartzJob job) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(job.getJobName(), job.getJobGroup());
this.scheduler.pauseJob(jobKey);
} /**
* 恢复任务
*
* @param job 调度信息
* @return 结果
*/
@Override
public void restartJob(QuartzJob job) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(job.getJobName(), job.getJobGroup());
this.scheduler.resumeJob(jobKey);
} /**
* 获取quartz调度器的计划任务
*
* @param job 调度信息
* @return 调度任务集合
*/
@Override
public List<QuartzJob> listJob(QuartzJob job) throws SchedulerException {
List<QuartzJob> scheduleJobVOList = new ArrayList<>();
GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
Set<JobKey> jobKeys = this.scheduler.getJobKeys(matcher);
for (JobKey jobKey : jobKeys) {
List<? extends Trigger> triggers = this.scheduler.getTriggersOfJob(jobKey);
for (Trigger trigger : triggers) {
JobDetail jobDetail = this.scheduler.getJobDetail(jobKey);
QuartzJob scheduleJobVO = (QuartzJob) jobDetail.getJobDataMap().get(ScheduleConstants.TASK_PROPERTIES);
Trigger.TriggerState triggerState = this.scheduler.getTriggerState(trigger.getKey());
scheduleJobVO.setStatus(triggerState.name());
// 判断trigger
if (trigger instanceof SimpleTrigger) {
SimpleTrigger simple = (SimpleTrigger) trigger;
scheduleJobVO.setCronExpression("重复次数:" + (simple.getRepeatCount() == -1 ? "无限" : simple.getRepeatCount()) + ",重复间隔:"
+ (simple.getRepeatInterval() / 1000L));
scheduleJobVO.setDescription(simple.getDescription());
}
if (trigger instanceof CronTrigger) {
CronTrigger cron = (CronTrigger) trigger;
scheduleJobVO.setCronExpression(cron.getCronExpression());
scheduleJobVO.setDescription(cron.getDescription() == null ? ("触发器:" + trigger.getKey()) : cron.getDescription());
}
scheduleJobVOList.add(scheduleJobVO);
}
}
return scheduleJobVOList;
}
}

ScheduleConfig

点击查看代码
package com.vipsoft.web.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean; import javax.sql.DataSource;
import java.util.Properties; @Configuration
public class ScheduleConfig { /**
* 设置属性
*/
private Properties quartzProperties() {
// quartz参数
Properties prop = new Properties();
prop.put("org.quartz.scheduler.instanceName", "VipSoftScheduler");
prop.put("org.quartz.scheduler.instanceId", "AUTO");
// 线程池配置
prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
prop.put("org.quartz.threadPool.threadCount", "20");
prop.put("org.quartz.threadPool.threadPriority", "5");
// JobStore配置
prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
// 集群配置
prop.put("org.quartz.jobStore.isClustered", "true");
prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000");
prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1");
prop.put("org.quartz.jobStore.txIsolationLevelSerializable", "true"); // sqlserver 启用
// prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?");
prop.put("org.quartz.jobStore.misfireThreshold", "12000");
prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
return prop;
} @Bean
public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setDataSource(dataSource); // //获取配置属性--通过加载配置文件的方式获取配置
// PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
// propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
// //在quartz.properties中的属性被读取并注入后再初始化对象
// propertiesFactoryBean.afterPropertiesSet();
// factory.setQuartzProperties(propertiesFactoryBean.getObject()); //用于quartz集群,加载quartz数据源配置
factory.setQuartzProperties(this.quartzProperties()); factory.setSchedulerName("VipSoftScheduler");
//QuartzScheduler 延时启动,应用启动完10秒后 QuartzScheduler 再启动
factory.setStartupDelay(10);
// 可选,QuartzScheduler
// 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
factory.setOverwriteExistingJobs(true);
// 设置自动启动,默认为true
factory.setAutoStartup(true);
factory.setApplicationContextSchedulerContextKey("applicationContextKey"); return factory;
}
}

暂停后,qrtz_triggers 表的 TRIGGER_STATE = PAUSED



运行效果

http://localhost:8088/schedule/add

http://localhost:8088/schedule/pause

http://localhost:8088/schedule/restart

{"jobName":"测试","jobGroup":"DEFAULT","invokeTarget":"scheduletask.execute('VipSoft Quartz')","cronExpression":"0/10 * * * * ?","misfirePolicy":2,"concurrent":1,"status":"0"}

参考

实现方式参考:若依(RuoYi),他是新建了一张中间表,通过 init() 方法,利用中心中间表进行定时任务的初始化

/**
* 项目启动时,初始化定时器
主要是防止手动修改数据库导致未同步到定时任务处理(注:不能手动修改数据库ID和任务组名,否则会导致脏数据)
*/
@PostConstruct
public void init() throws SchedulerException, TaskException
{
scheduler.clear();
List<SysJob> jobList = jobMapper.selectJobAll();
for (SysJob job : jobList)
{
ScheduleUtils.createScheduleJob(scheduler, job);
}
}

SpringBoot 集成 Quartz + MySQL的更多相关文章

  1. Springboot集成mybatis(mysql),mail,mongodb,cassandra,scheduler,redis,kafka,shiro,websocket

    https://blog.csdn.net/a123demi/article/details/78234023  : Springboot集成mybatis(mysql),mail,mongodb,c ...

  2. SpringBoot集成Quartz实现定时任务

    1 需求 在我的前后端分离的实验室管理项目中,有一个功能是学生状态统计.我的设计是按天统计每种状态的比例.为了便于计算,在每天0点,系统需要将学生的状态重置,并插入一条数据作为一天的开始状态.另外,考 ...

  3. SpringBoot集成Quartz(解决@Autowired空指针Null问题即依赖注入的属性为null)

    使用spring-boot作为基础框架,其理念为零配置文件,所有的配置都是基于注解和暴露bean的方式. Quartz的4个核心概念: 1.Job表示一个工作,要执行的具体内容.此接口中只有一个方法v ...

  4. Springboot集成Quartz

    之前学习过spring的定时任务 :https://www.cnblogs.com/slimshady/p/10112515.html 本文主要学习记录下springboot使用quartz 1.   ...

  5. springBoot集成 quartz动态定时任务

    项目中需要用到定时任务,考虑了下java方面定时任务无非就三种: 用Java自带的timer类.稍微看了一下,可以实现大部分的指定频率的任务的调度(timer.schedule()),也可以实现关闭和 ...

  6. Spring Boot笔记(三) springboot 集成 Quartz 定时任务

    个人博客网:https://wushaopei.github.io/    (你想要这里多有) 1. 在 pom.xml 中 添加 Quartz 所需要 的 依赖 <!--定时器 quartz- ...

  7. SpringBoot集成Quartz实现定时器

    SpringBoot+Quartz实现定时器,由于本人也是刚学习,不足之处请各位大神指正 .. 1.pom配置   <dependency>   <groupId>org.sp ...

  8. springboot集成quartz定时任务课动态执行

    <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</ ...

  9. springboot集成quartz实现任务调度

    quartz 概述 特点 强大的调度功能 灵活的应用方式 分布式和集群能力 用到的设计模式 Builder 模式 factory模式 组件模式 链式写法 体系结构 调度器 任务 触发器 架构图 spr ...

  10. springboot自带定时任务和集成quartz

    1,springboot自带的定时任务  默认是单线程 有这个依赖就可以 <dependency> <groupId>org.springframework.boot</ ...

随机推荐

  1. oracle vm virtualBox 虚拟机共享磁盘后报错

    不能为虚拟电脑 xxx2打开一个新任务. Locking of attached media failed. A possible reason is that one of the media is ...

  2. jdbc连接数据库access denied for user 'root'@'localhost'(using password:YES)

    navicat可以进行连接,一般原因为mysql未启用远程连接 以下为解决方案 解决方法-更新用户加密方式: MySQL [mysql]> ALTER USER 'root'@'%' IDENT ...

  3. 10.14 2020 实验 7:OpenDaylight 实验——Python 中的 REST API 调用

    一.实验目的  对 Python 调用 OpenDaylight 的 REST API 方法有初步了解.   二.实验任务  本实验需要用另一种方法完成上一个实验相同的功能,即通过 Python 程序 ...

  4. ABP vNext微服务架构详细教程——分布式权限框架

    1.简介 ABP vNext框架本身提供了一套权限框架,其功能非常丰富,具体可参考官方文档:https://docs.abp.io/en/abp/latest/Authorization 但是我们使用 ...

  5. .net Core使用Knife4jUI更换Swagger皮肤

    Knife4j的前身是swagger-bootstrap-ui,前身swagger-bootstrap-ui是一个纯swagger-ui的ui皮肤项目 官网实战指南:https://doc.xiaom ...

  6. Docker-界面化

    docker-ui docker-ui只能便于我们查看系统的相关信息,镜像或者容器.在界面上做简单的处理,可处理单机版Docker. docker pull uifd/ui-for-docker do ...

  7. Checkmk监控工具使用手册

    其实用法Checkmk官网文档很全面:https://docs.checkmk.com/latest/en/intro_setup.html 顺着beginner's guide章节看完基本就能上手, ...

  8. 接口自动化之request几种常见请求及响应方法

    request 的几种常见方法 1.request.get() 发送get请求 2.request.post() 发送post请求 3.request.delete() 发送delete请求 4.re ...

  9. BitBake使用攻略--BitBake的语法知识二

    目录 写在前面 1. BitBake中的任务 2. 任务配置 2.1 依赖 2.1.1 内部任务间的依赖 2.1.2 不同菜谱下的任务间依赖 2.1.3 运行时态下的依赖 2.1.4 递归依赖 2.1 ...

  10. jsp页面中的正则表达式--主要用于js判断文本格式

    一.方括号[] 举例: 二.^ 三.元字符 举例的话,就可以这么说,要实现要表示整数的话: []就表示输入的文本框里面的数字的第一位,可以这么写--->[1-9] 然后已知\d表示的与[0-9] ...