SpringBoot3集成Quartz
标签:Quartz.Job.Scheduler;
一、简介
Quartz由Java编写的功能丰富的开源作业调度框架,可以集成到几乎任何Java应用程序中,并且能够创建多个作业调度;
在实际的业务中,有很多场景依赖定时任务,比如常见的:订单超时处理,数据报表统计分析,会员等周期性管理,业务识别和预警通知等;
二、工程搭建
1、工程结构

2、依赖管理
在starter-quartz组件中,实际依赖的是quartz组件2.3.2版本,使用Quartz框架时,需要自定义任务和执行逻辑,以更加灵活的方式管理业务调度;
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
<version>${spring-boot.version}</version>
</dependency>
3、数据库
Quartz框架使用的表结构在如图的路径下,本文选择MySQL数据库存储,除此之外自定义两张表:quartz_job任务表和quartz_log任务执行日志表;

4、配置文件
在配置文件中使用Druid组件连接boot-quartz数据库,对于Quartz框架,主要配置数据库存储,调度器的基础信息,以及执行任务的线程池;
spring:
# 定时器配置
quartz:
# 使用数据库存储
job-store-type: jdbc
# 初始化完成后自动启动调度程序
autoStartup: true
properties:
org:
quartz:
# 调度器配置
scheduler:
instanceName: bootQuartzScheduler
instanceId: AUTO
# 存储配置
jobStore:
class: org.springframework.scheduling.quartz.LocalDataSourceJobStore
driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
tablePrefix: qrtz_
isClustered: true
misfireThreshold: 12000
clusterCheckinInterval: 15000
useProperties: false
# 线程池配置
threadPool:
threadNamePrefix: Boot_Job_Pool
threadPriority: 5
threadCount: 10
class: org.quartz.simpl.SimpleThreadPool
三、Quartz用法
对于任务管理的相关Web接口,采用Swagger文档组件,接口和实体类添加注解后,访问IP:Port/swagger-ui/index.html地址即可;

1、初始化加载
在服务启动时执行init初始化方法,查询quartz_job表中运行和暂停状态的任务,判断触发器是否存在,如果不存在则创建,如果存在则更新;
@Service
public class QuartzJobService {
@Resource
private QuartzJobMapper quartzJobMapper ;
@Resource
private QuartzManage quartzManage;
@PostConstruct
public void init () {
LambdaQueryWrapper<QuartzJob> queryWrapper = new LambdaQueryWrapper<>() ;
queryWrapper.in(QuartzJob::getState,JobState.JOB_RUN.getStatus(),JobState.JOB_STOP.getStatus());
List<QuartzJob> jobList = quartzJobMapper.selectList(queryWrapper);
jobList.forEach(quartzJob -> {
CronTrigger cronTrigger = quartzManage.getCronTrigger(quartzJob.getId()) ;
if (Objects.isNull(cronTrigger)){
quartzManage.createJob(quartzJob);
} else {
quartzManage.updateJob(quartzJob);
}
});
}
}
2、新增任务
在创建任务时,需要定义JobKey和TriggerKey的构建规则,Key需要具备唯一性,通常使用任务表的主键ID,任务一般是基于Cron表达式被调度执行的;
@Component
public class QuartzManage {
@Resource
private Scheduler scheduler ;
public void createJob (QuartzJob quartzJob){
try {
// 构建任务
JobDetail jobDetail = JobBuilder.newJob(QuartzRecord.class).withIdentity(getJobKey(quartzJob.getId())).build() ;
// 构建Cron调度器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder
.cronSchedule(quartzJob.getCronExpres())
.withMisfireHandlingInstructionDoNothing() ;
// 任务触发器
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity(getTriggerKey(quartzJob.getId()))
.withSchedule(scheduleBuilder).build() ;
jobDetail.getJobDataMap().put(QuartzJob.JOB_PARAM_KEY,quartzJob);
scheduler.scheduleJob(jobDetail,trigger) ;
// 状态校验
checkStop(quartzJob) ;
} catch (SchedulerException e){
throw new RuntimeException("createJob Fail",e) ;
}
}
}
3、更新任务
先通过任务ID查询TriggerKey,对于更新来说,最常见的就是Cron表达式即调度规则的更新,或者任务的执行参数更新;
@Component
public class QuartzManage {
@Resource
private Scheduler scheduler ;
public void updateJob(QuartzJob quartzJob) {
try {
// 查询触发器Key
TriggerKey triggerKey = getTriggerKey(quartzJob.getId());
// 构建Cron调度器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder
.cronSchedule(quartzJob.getCronExpres())
.withMisfireHandlingInstructionDoNothing();
// 任务触发器
CronTrigger trigger = getCronTrigger(quartzJob.getId())
.getTriggerBuilder().withIdentity(triggerKey)
.withSchedule(scheduleBuilder).build();
trigger.getJobDataMap().put(QuartzJob.JOB_PARAM_KEY, quartzJob);
scheduler.rescheduleJob(triggerKey, trigger);
// 状态校验
checkStop(quartzJob) ;
} catch (SchedulerException e) {
throw new RuntimeException("updateJob Fail",e) ;
}
}
}
4、暂停任务
先通过任务ID查询JobKey,判断任务是非运行状态,则停止任务;
@Component
public class QuartzManage {
@Resource
private Scheduler scheduler ;
public void checkStop (QuartzJob quartzJob){
try {
if(quartzJob.getState() != JobState.JOB_RUN.getStatus()){
this.scheduler.pauseJob(getJobKey(quartzJob.getId()));
}
} catch (SchedulerException e){
throw new RuntimeException("pauseJob Fail",e) ;
}
}
}
5、恢复任务
先通过任务ID查询JobKey,恢复任务正常执行;
@Component
public class QuartzManage {
@Resource
private Scheduler scheduler ;
public void resumeJob (Integer jobId){
try {
this.scheduler.resumeJob(getJobKey(jobId));
} catch (SchedulerException e){
throw new RuntimeException("resumeJob Fail",e) ;
}
}
}
6、执行一次
传入任务主体,再通过任务ID查询JobKey,然后立即执行一次任务;
@Component
public class QuartzManage {
@Resource
private Scheduler scheduler ;
public void run (QuartzJob quartzJob){
try {
JobDataMap dataMap = new JobDataMap() ;
dataMap.put(QuartzJob.JOB_PARAM_KEY,quartzJob);
this.scheduler.triggerJob(getJobKey(quartzJob.getId()),dataMap);
} catch (SchedulerException e){
throw new RuntimeException("run Fail",e) ;
}
}
}
7、删除任务
先通过任务ID查询JobKey,彻底删除任务;
@Component
public class QuartzManage {
@Resource
private Scheduler scheduler ;
public void deleteJob (Integer jobId){
try {
scheduler.deleteJob(getJobKey(jobId));
} catch (SchedulerException e){
throw new RuntimeException("deleteJob Fail",e) ;
}
}
}
8、任务执行
Quartz被集成在Spring框架之后,任务类自然会以Bean对象的方式被管理,在任务创建时,设置要执行的作业类QuartzRecord,该类继承QuartzJobBean抽象类,通过重写executeInternal方法,来管理任务实际执行的逻辑;
public class QuartzRecord extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext context) {
QuartzJob quartzJob = (QuartzJob)context.getMergedJobDataMap().get(QuartzJob.JOB_PARAM_KEY) ;
QuartzLogService quartzLogService = (QuartzLogService)SpringContextUtil.getBean("quartzLogService") ;
// 定时器日志记录
QuartzLog quartzLog = new QuartzLog () ;
quartzLog.setJobId(quartzJob.getId());
quartzLog.setBeanName(quartzJob.getBeanName());
quartzLog.setParams(quartzJob.getParams());
quartzLog.setCreateTime(new Date());
long beginTime = System.currentTimeMillis() ;
try {
// 加载并执行
Object target = SpringContextUtil.getBean(quartzJob.getBeanName());
Method method = target.getClass().getDeclaredMethod("run", String.class);
method.invoke(target, quartzJob.getParams());
long executeTime = System.currentTimeMillis() - beginTime;
quartzLog.setTimes((int)executeTime);
quartzLog.setState(LogState.LOG_SUS.getStatus());
} catch (Exception e){
// 异常信息
long executeTime = System.currentTimeMillis() - beginTime;
quartzLog.setTimes((int)executeTime);
quartzLog.setState(LogState.LOG_FAIL.getStatus());
quartzLog.setError(e.getMessage());
} finally {
// 保存执行日志
quartzLogService.insert(quartzLog) ;
}
}
}
四、参考源码
文档仓库:
https://gitee.com/cicadasmile/butte-java-note
源码仓库:
https://gitee.com/cicadasmile/butte-spring-parent
SpringBoot3集成Quartz的更多相关文章
- Spring集成Quartz定时器
<!-- Spring集成Quartz开始 --> <bean id="startQuertz" lazy-init="false" auto ...
- spring集成quartz
spring集成quartz 注意:出现异常"Caused by: java.lang.IncompatibleClassChangeError: class org.springframe ...
- spring boot1.0 集成quartz 动态配置定时任务
转载自 https://www.imooc.com/article/36278 一.Quartz简介了解 Quartz Quartz 是一个完全由 Java 编写的开源作业调度框架,为在 Java 应 ...
- springboot自带定时任务和集成quartz
1,springboot自带的定时任务 默认是单线程 有这个依赖就可以 <dependency> <groupId>org.springframework.boot</ ...
- Spring 集成Quartz
在使用jdk的timer时发现无法在指定的日期进行执行任务.这便引入一个优秀的开源任务调度框架“quartz”.这里使用的是quartz-1.8.6版本.Quart的官网:http://www.qua ...
- ssh中使用spring的集成quartz 编写定时任务
之前没有使用框架开发时对于开发定时任务都是 使用java的原声timer类,重写线程的run方法跑要执行的任务.刚刚换的新公司,项目使用ssh2,目前该项目中的定时任务的使用spirng集成的quar ...
- SpringBoot集成Quartz(解决@Autowired空指针Null问题即依赖注入的属性为null)
使用spring-boot作为基础框架,其理念为零配置文件,所有的配置都是基于注解和暴露bean的方式. Quartz的4个核心概念: 1.Job表示一个工作,要执行的具体内容.此接口中只有一个方法v ...
- (十七)Spring 集成Quartz
在使用jdk的timer时发现无法满足这次的开发需求:即无法在指定的日期进行执行任务.这便引入一个优秀的开源任务调度框架“quartz”.这里加入的是quartz-1.8.6版本.Quart的官网:h ...
- Spring Boot集成quartz实现定时任务并支持切换任务数据源
org.quartz实现定时任务并自定义切换任务数据源 在工作中经常会需要使用到定时任务处理各种周期性的任务,org.quartz是处理此类定时任务的一个优秀框架.随着项目一点点推进,此时我们并不满足 ...
- Spring/Spring boot正确集成Quartz及解决@Autowired失效问题
周五检查以前Spring boot集成Quartz项目的时候,发现配置错误,因此通过阅读源码的方式,探索Spring正确集成Quartz的方式. 问题发现 检查去年的项目代码,发现关于QuartzJo ...
随机推荐
- 2021-03-24:给定一个整数组成的无序数组arr,值可能正、可能负、可能0。给定一个整数值K,找到arr的所有子数组里,哪个子数组的累加和等于K,并且是长度最大的。返回其长度。
2021-03-24:给定一个整数组成的无序数组arr,值可能正.可能负.可能0.给定一个整数值K,找到arr的所有子数组里,哪个子数组的累加和等于K,并且是长度最大的.返回其长度. 福大大 答案20 ...
- 2022-02-10:k8s安装mongo,yaml如何写?
2022-02-10:k8s安装mongo,yaml如何写? 答案2022-02-10: yaml如下: apiVersion: v1 kind: Service metadata: labels: ...
- 用go设计开发一个自己的轻量级登录库/框架吧(业务篇)
用go设计开发一个自己的轻量级登录库/框架吧(业务篇) 本篇会讲讲框架的登录业务的实现.实现三种登录模式: 同一用户只能登录一次 同一用户多次登录多token 同一用户多次登录共享一个token 源码 ...
- save() prohibited to prevent data loss due to unsaved related object 'item_n
问题描述: save() prohibited to prevent data loss due to unsaved related object 'item_no 原因分析: 原来的目的是保存数据 ...
- Linux 内存管理 pt.3
哈喽大家好,我是咸鱼 在<Linux 内存管理 pt.2>中我们学习了多级页表和大页,我们知道了由于历史遗留的问题,Linux 的页通常为 4KB 这样就会导致一个页表里面会有特别多页,为 ...
- 避坑版-OpenSSH 用户名枚举漏洞(CVE-2018-15473)
介绍: OpenSSH 7.7前存在一个用户名枚举漏洞,通过该漏洞,攻击者可以判断某个用户名是否存在于目标主机中,在复现过程中遇到了很多坑,这里我就把遇到坑和解决方法告诉大家!!! 漏洞环境: 靶机: ...
- Springcloud 开始来了解
为什么要学习springcloud? "微服务 "一词源于Martin Fowler 的名为 Microservices 的博文,简单地说, 微服务是系统架构上的一种设计风格, 它 ...
- 使用android studio发布android与flutter混合开发项目
一.生成androd签名证书 Android studio的Build > Grenerate Signed Bundle 选择apk 点击Create New 录入对应的签名信息:点击确定 对 ...
- celery 组件在django环境应用
第一步安装 pip install celery==4.4 第二步 配置环境 # ############################# celery 配置连接redis ############ ...
- python 学习 ---函数(带参数)
函数式编程最重要的是增强代码的重用性和可读性 1 def 函数名(参数): 2 3 ... 4 函数体 5 ... 函数的定义主要有如下要点: def:表示函数的关键字 函数名:函数的名称,日后根据函 ...