quartz官方文档:http://www.quartz-scheduler.org/documentation/
Quartz 是一个功能丰富的开源任务调度库,可以集成到任何 Java 应用程序中——从最小的独立应用程序到最大的电子商务系统。Quartz 可用于创建简单或复杂的计划,以执行数十、数百甚至数万个作业; 这些作业的任务被定义为标准的 Java 组件,可以编程让它们执行任何任务。Quartz Scheduler 包含许多企业级特性,例如对 JTA 事务和集群的支持。
集成spring boot步骤:
添加依赖
<!--定时任务-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
添加配置
添加的配置项包括持久化任务,配置任务的线程,任务随应用启动等。
spring:
quartz:
properties:
org:
quartz:
scheduler:
#调度程序名称
instanceName: workScheduler
instanceId: AUTO
jobStore:
#无需将调度事务绑定到其它事务,使用JobStoreTX
class: org.quartz.impl.jdbcjobstore.JobStoreTX
#跟数据库初始化脚本中配置保持一致
driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#Quartz 表的前缀
tablePrefix: QRTZ_
#设置为“true”以打开集群特性。如果 Quartz 的多个实例使用同一组数据库表,那么这个属性必须设置为“ true”
isClustered: true
#与集群其它实例检查的频率(ms)
clusterCheckinInterval: 10000
#为true则JobDataMaps中类型都为string,不用存BLOB
useProperties: false
threadPool:
class: org.quartz.simpl.SimpleThreadPool
#可用于并发执行作业的线程数
threadCount: 20
#线程优先级
threadPriority: 5
threadsInheritContextClassLoaderOfInitializingThread: true
job-store-type: jdbc
#覆盖已有任务
overwrite-existing-jobs: true
#自动启动
auto-startup: true
添加持久化任务数据表
需要这些数据表来持久化任务
运行tables_mysql_innodb.sql创建Job Stores所需的数据表,sql文件在软件包中
数据表用途:
qrtz_blob_triggers : 以Blob 类型存储的触发器。
qrtz_calendars:存放日历信息, quartz可配置一个日历来指定一个时间范围。
qrtz_cron_triggers:存放cron类型的触发器。
qrtz_fired_triggers:存放已触发的触发器。
qrtz_job_details:存放一个JobDetail信息。
qrtz_job_listeners:job监听器。
qrtz_locks: 存储程序的悲观锁的信息(假如使用了悲观锁)。
qrtz_paused_trigger_graps:存放暂停掉的触发器。
qrtz_scheduler_state:调度器状态。
qrtz_simple_triggers:简单触发器的信息。
qrtz_trigger_listeners:触发器监听器。
qrtz_triggers:触发器的基本信息。
cron方式需要用到的4张数据表:
qrtz_triggers,qrtz_cron_triggers,qrtz_fired_triggers,qrtz_job_details
创建任务监听器
可以监听任务调度前后和任务被拒绝
package com.training.examination.work.quartz;
import org.jboss.logging.Logger;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobListener;
public class SchedulerListener implements JobListener {
public static final String LISTENER_NAME = "QuartSchedulerListener";
private static final Logger logger = Logger.getLogger(SchedulerListener.class);
@Override
public String getName() {
return LISTENER_NAME;
}
/**
* 任务被调度前
*
* @param context
*/
@Override
public void jobToBeExecuted(JobExecutionContext context) {
String jobName = context.getJobDetail().getKey().toString();
logger.info("Job: " + jobName + " 开始执行");
}
/**
* 任务调度被拒绝
*
* @param context
*/
@Override
public void jobExecutionVetoed(JobExecutionContext context) {
String jobName = context.getJobDetail().getKey().toString();
logger.info("Job: " + jobName + " 被拒绝");
}
/**
* 任务被调度后
*
* @param context
* @param jobException
*/
@Override
public void jobWasExecuted(JobExecutionContext context,
JobExecutionException jobException) {
String jobName = context.getJobDetail().getKey().toString();
logger.info("Job: " + jobName + " 执行结束");
if (jobException != null && !"".equals(jobException.getMessage())) {
logger.error("Exception thrown by: " + jobName
+ " Exception: " + jobException.getMessage());
}
}
}
创建任务调度器
调度包括任务的启动、停止、暂停、恢复
package com.training.examination.work.quartz;
import com.alibaba.fastjson.JSON;
import lombok.RequiredArgsConstructor;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Map;
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class SchedulerManager {
private final Scheduler scheduler;
private JobListener scheduleListener;
/**
* 开始定时任务
*
* @param jobName
* @param jobGroup
* @throws SchedulerException
*/
public void startJob(String cron, String jobName, String jobGroup, Class<? extends Job> jobClass, String parameter) throws SchedulerException {
if (scheduleListener == null) {
scheduleListener = new SchedulerListener();
scheduler.getListenerManager().addJobListener(scheduleListener);
}
JobKey jobKey = new JobKey(jobName, jobGroup);
if (!scheduler.checkExists(jobKey)) {
/*
* 此处可以先通过任务名查询数据库,如果数据库中存在该任务,更新任务的配置以及触发器
* 如果此时数据库中没有查询到该任务,则按照下面的步骤新建一个任务,并配置初始化的参数,并将配置存到数据库中
*/
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroup).build();
JobDataMap jobDataMap = jobDetail.getJobDataMap();
Map map = (Map) JSON.parse(parameter);
for (Object object : map.keySet()) {
jobDataMap.put(object.toString(), map.get(object).toString());
}
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(
jobName, jobGroup).withSchedule(scheduleBuilder).build();
scheduler.scheduleJob(jobDetail, cronTrigger);
}
}
/**
* 移除定时任务
*
* @param jobName
* @param jobGroup
* @throws SchedulerException
*/
public void deleteJob(String jobName, String jobGroup) throws SchedulerException {
JobKey jobKey = new JobKey(jobName, jobGroup);
scheduler.deleteJob(jobKey);
}
/**
* 暂停定时任务
*
* @param jobName
* @param jobGroup
* @throws SchedulerException
*/
public void pauseJob(String jobName, String jobGroup) throws SchedulerException {
JobKey jobKey = new JobKey(jobName, jobGroup);
scheduler.pauseJob(jobKey);
}
/**
* 恢复定时任务
*
* @param jobName
* @param jobGroup
* @throws SchedulerException
*/
public void resumeJob(String jobName, String jobGroup) throws SchedulerException {
JobKey triggerKey = new JobKey(jobName, jobGroup);
scheduler.resumeJob(triggerKey);
}
/**
* 清空所有当前scheduler对象下的定时任务【目前只有全局一个scheduler对象】
*
* @throws SchedulerException
*/
public void clearAll() throws SchedulerException {
scheduler.clear();
}
}
创建任务执行类
也就是任务本身
package com.training.examination.work.quartz.jobs;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.training.examination.core.common.Constant;
import com.training.examination.core.util.DateUtil;
import com.training.examination.work.entity.ExamManageConfig;
import com.training.examination.work.entity.ExamRecord;
import com.training.examination.work.entity.MonthTrainingPlan;
import com.training.examination.work.service.IExamManageConfigService;
import com.training.examination.work.service.IExamService;
import com.training.examination.work.service.IMonthTrainingPlanService;
import com.training.examination.work.service.INoticeService;
import com.training.examination.work.vo.ExamRecordVO;
import org.jboss.logging.Logger;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.stream.Collectors;
@Component
public class NoticeJob implements Job {
//这里注入需要的service
@Autowired
private IExamManageConfigService examManageConfigService;
private static final Logger logger = Logger.getLogger(NoticeJob.class);
@Override
public void execute(JobExecutionContext jobExecutionContext) {
//获取任务参数
JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();
long param1 = jobDataMap.getLongValue("param1");
//执行任务逻辑...
}
}
Cron表达式
Cron表达式是一个字符串,字符串以5或6个空格隔开,分为6或7个域,每一个域代表一个含义,Cron有如下两种语法格式:
Seconds Minutes Hours DayOfMonth Month DayOfWeek Year
或
Seconds Minutes Hours DayOfMonth Month DayOfWeek
每一个域可出现的字符如下:
Seconds:可出现", - * /“四个字符,有效范围为0-59的整数
Minutes:可出现”, - * /“四个字符,有效范围为0-59的整数
Hours:可出现”, - * /“四个字符,有效范围为0-23的整数
DayOfMonth:可出现”, - * / ? L W C"八个字符,有效范围为0-31的整数
Month:可出现", - * /“四个字符,有效范围为1-12的整数或JAN-DEc
DayOfWeek:可出现”, - * / ? L C #“四个字符,有效范围为1-7的整数或SUN-SAT两个范围。1表示星期天,2表示星期一, 依次类推
Year:可出现”, - * /"四个字符,有效范围为1970-2099年
如项目中每天早上8点运行任务,则表达式为
0 0 8 1/1 * ? *