Quartz 是一个完全由 Java 编写的开源作业调度框架,为在 Java 应用程序中进行作业调度提供了简单却强大的机制。该文章不过多介绍Quartz的概念,主要做一个封装的记录。
创建一个简单的SpringBoot项目,pom如下:
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.6.1
org.demo
quartz
0.0.1-SNAPSHOT
quartz
Demo project for Spring Boot
11
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-starter-quartz
org.projectlombok
lombok
org.springframework.boot
spring-boot-maven-plugin
Quartz可以通过org.quartz.Trigger.class
去拓展定时类型的,目前需求需要支持CRON和固定间隔两种定时类型,后期需要支持跳过节假日、周一~周五、周末等需求,所以需要让定时类型支持拓展。
package org.demo.quartz.mode;
/**
* @author Xiaohan.Yuan
* @version 1.0.0
* @ClassName TimingTriggerType.java
* @Description 定时类型触发器类型
* @createTime 2021年12月16日
*/
public enum TriggerType {
CRON("标准CRON支持"),
INTERVAL_MILLISECOND("固定间隔毫秒"),
INTERVAL_SECOND("固定间隔秒"),
INTERVAL_MINUTE("固定间隔分钟"),
INTERVAL_HOUR("固定间隔小时"),
WEEKDAYS("工作日,跳过节假日"),
HOLIDAY("节假日")
;
private String describe;
TriggerType(String describe) {
this.describe = describe;
}
}
我们需要构建不同的定时类型,不同的定时类型需要的参数也是不同的,因此我们需要抽象出定时的公用参数,将不同的参数多态实现。
package org.demo.quartz.mode;
import lombok.Getter;
import lombok.Setter;
import org.demo.quartz.task.QuartzTaskJob;
import java.util.Map;
/**
* @author Xiaohan.Yuan
* @version 1.0.0
* @ClassName TimingModel.java
* @Description 构建定时的model
* @createTime 2021年12月16日
*/
@Getter
@Setter
public class TimingModel {
/**
* 该定时的任务处理器
*/
private Class extends QuartzTaskJob> taskClass;
/**
* 任务名
*/
private String taskName;
/**
* 任务组名
* */
private String groupName;
/**
* 任务描述
* */
private String description;
/**
* 任务类型
*/
private TriggerType type;
/**
* 任务参数,可在具体的QuartzTaskJob实现中获取这些参数
* */
private Map param;
/**
* 任务状态
* */
private String taskStatus;
public TimingModel(Class extends QuartzTaskJob> taskClass, String taskName, String groupName, String description, TriggerType type, Map param) {
this.taskClass = taskClass;
this.taskName = taskName;
this.groupName = groupName;
this.description = description;
this.type = type;
this.param = param;
}
public TimingModel(Class extends QuartzTaskJob> taskClass, String taskName, String groupName, String description, TriggerType type) {
this.taskClass = taskClass;
this.taskName = taskName;
this.groupName = groupName;
this.description = description;
this.type = type;
}
}
package org.demo.quartz.mode;
import lombok.Getter;
import lombok.Setter;
import org.demo.quartz.task.QuartzTaskJob;
import java.util.Map;
/**
* @author Xiaohan.Yuan
* @version 1.0.0
* @ClassName CronTimingModel.java
* @Description cron触发器model
* @createTime 2021年12月16日
*/
@Getter
@Setter
public class CronTimingModel extends TimingModel{
/**
* cron表达式
* */
private String cronExpression;
public CronTimingModel(Class extends QuartzTaskJob> taskClass, String taskName, String groupName, String description, Map param,String cronExpression) {
super(taskClass, taskName, groupName, description, TriggerType.CRON, param);
this.cronExpression = cronExpression;
}
public CronTimingModel(Class extends QuartzTaskJob> taskClass, String taskName, String groupName, String description,String cronExpression) {
super(taskClass, taskName, groupName, description, TriggerType.CRON);
this.cronExpression = cronExpression;
}
}
package org.demo.quartz.mode;
import lombok.Getter;
import lombok.Setter;
import org.demo.quartz.exception.TimingException;
import org.demo.quartz.task.QuartzTaskJob;
import java.util.Map;
/**
* @author Xiaohan.Yuan
* @version 1.0.0
* @ClassName IntervalTimingMode.java
* @Description
* @createTime 2021年12月16日
*/
@Getter
@Setter
public class IntervalTimingMode extends TimingModel {
/**
* 事件间隔,根据TriggerType确定单位,除了数值为毫秒,该数值必须在-2^32~2^31 (-2147483648 ~ 2147483647)
* */
private Long interval;
/**
* 重复次数,会执行该数值+1次,为空无限重复
* */
private Integer repeatCount;
public IntervalTimingMode(Class extends QuartzTaskJob> taskClass, String taskName, String groupName, String description, TriggerType type, Map param, Long interval,Integer repeatCount) {
super(taskClass, taskName, groupName, description, type, param);
if (type != TriggerType.INTERVAL_MILLISECOND){
if (interval<(-2^32)||interval>(2^31)){
throw new TimingException("interval超出范围,除了类型为INTERVAL_MILLISECOND的数据间隔定时的interval范围必须在-2^32~2^31 (-2147483648 ~ 2147483647)");
}
}
this.interval = interval;
this.repeatCount = repeatCount;
}
public IntervalTimingMode(Class extends QuartzTaskJob> taskClass, String taskName, String groupName, String description, TriggerType type, Long interval,Integer repeatCount) {
super(taskClass, taskName, groupName, description, type);
if (type != TriggerType.INTERVAL_MILLISECOND){
if (interval<(-2^32)||interval>(2^31)){
throw new TimingException("interval超出范围,除了类型为INTERVAL_MILLISECOND的数据间隔定时的interval范围必须在-2^32~2^31 (-2147483648 ~ 2147483647)");
}
}
this.interval = interval;
this.repeatCount = repeatCount;
}
}
package org.demo.quartz.task;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
/**
* @author Xiaohan.Yuan
* @version 1.0.0
* @ClassName QuartzTaskJob.java
* @Description
* @createTime 2021年12月16日
*/
public interface QuartzTaskJob extends Job {
@Override
void execute(JobExecutionContext context) throws JobExecutionException;
}
package org.demo.quartz.task;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.SimpleTrigger;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class TestQuartz implements QuartzTaskJob {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// 获取参数
JobDataMap jobDataMap = context.getTrigger().getJobDataMap();
// 获取任务名
String name = context.getJobDetail().getJobBuilder().build().getKey().getName();
// 获取任务分组
String group = context.getJobDetail().getJobBuilder().build().getKey().getGroup();
// 获取任务描述
String description = context.getJobDetail().getDescription();
if (context.getTrigger() instanceof SimpleTrigger){
// 运行次数
System.out.println(((SimpleTrigger)context.getTrigger()).getTimesTriggered());
}
log.info("----------------------" +
"
任务组:{}
任务名:{}
任务描述:{}
获取参数paramKey:{}
" +
"----------------------"
,name,group,description,jobDataMap.getString("paramKey"));
try {
// QuartzJobManager.getInstance().jobdelete(this.getClass().getSimpleName(),"ah");//执行完此任务就删除自己
} catch (Exception e) {
e.printStackTrace();
}
}
}
package org.demo.quartz.trigger;
import org.demo.quartz.mode.TriggerType;
import org.demo.quartz.mode.TimingModel;
import org.quartz.Trigger;
/**
* @author Xiaohan.Yuan
* @version 1.0.0
* @ClassName TriggerHandler.java
* @Description 触发器工厂
* @createTime 2021年12月16日
*/
public interface ITriggerFactory {
/**
* 判断是否为该类型的触发器
*
* @param triggerType 触发器类型
* @return boolean 如果是该类型的触发器返回true 否则返回false
* @author YuanXiaohan
* @date 2021/12/16 2:33 下午
*/
public boolean check(TriggerType triggerType);
public Trigger build(TimingModel timingModel);
}
package org.demo.quartz.trigger.factory;
import org.demo.quartz.exception.TimingException;
import org.demo.quartz.mode.CronTimingModel;
import org.demo.quartz.mode.TriggerType;
import org.demo.quartz.mode.TimingModel;
import org.demo.quartz.trigger.ITriggerFactory;
import org.quartz.CronScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.springframework.stereotype.Component;
/**
* @author Xiaohan.Yuan
* @version 1.0.0
* @ClassName CronTrigger.java
* @Description
* @createTime 2021年12月16日
*/
@Component
public class CronTrigger implements ITriggerFactory {
@Override
public boolean check(TriggerType triggerType) {
return triggerType==TriggerType.CRON;
}
@Override
public Trigger build(TimingModel timingModel) {
if (!(timingModel instanceof CronTimingModel)){
throw new TimingException("构建类型为CRON定时必须传入CronTimingModel.class的实现类");
}
//按新的cronExpression表达式构建一个新的trigger
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(((CronTimingModel) timingModel).getCronExpression());
return TriggerBuilder.newTrigger().withIdentity(timingModel.getTaskName(), timingModel.getTaskName())
.withSchedule(scheduleBuilder).build();
}
}
package org.demo.quartz.trigger.factory;
import org.demo.quartz.exception.TimingException;
import org.demo.quartz.mode.IntervalTimingMode;
import org.demo.quartz.mode.TimingModel;
import org.demo.quartz.mode.TriggerType;
import org.demo.quartz.trigger.ITriggerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.springframework.stereotype.Component;
/**
* @author Xiaohan.Yuan
* @version 1.0.0
* @ClassName IntervalTrigger.java
* @Description
* @createTime 2021年12月16日
*/
@Component
public class IntervalTrigger implements ITriggerFactory {
@Override
public boolean check(TriggerType triggerType) {
return triggerType == TriggerType.INTERVAL_MINUTE || triggerType == TriggerType.INTERVAL_SECOND || triggerType == TriggerType.INTERVAL_MILLISECOND||triggerType == TriggerType.INTERVAL_HOUR;
}
@Override
public Trigger build(TimingModel timingModel) {
if (!(timingModel instanceof IntervalTimingMode)){
throw new TimingException("构建类型为INTERVAL定时必须传入IntervalTimingMode.class的实现类");
}
//创建触发器
SimpleScheduleBuilder simpleScheduleBuilder = SimpleScheduleBuilder.simpleSchedule();
Long interval = ((IntervalTimingMode) timingModel).getInterval();
Integer repeatCount = ((IntervalTimingMode) timingModel).getRepeatCount();
switch (timingModel.getType()){
case INTERVAL_MINUTE:
simpleScheduleBuilder.withIntervalInMinutes(Math.toIntExact(interval));
break;
case INTERVAL_HOUR:
simpleScheduleBuilder.withIntervalInHours(Math.toIntExact(interval));
break;
case INTERVAL_SECOND:
simpleScheduleBuilder.withIntervalInSeconds(Math.toIntExact(interval));
break;
case INTERVAL_MILLISECOND:
simpleScheduleBuilder.withIntervalInMilliseconds(interval);
break;
}
if (repeatCount==null){
// 无限重复
simpleScheduleBuilder.repeatForever();
}else {
simpleScheduleBuilder.withRepeatCount(repeatCount);
}
return TriggerBuilder.newTrigger().withIdentity(timingModel.getTaskName(), timingModel.getTaskName())
.withSchedule(simpleScheduleBuilder).build();
}
}
package org.demo.quartz.trigger;
import org.demo.quartz.mode.TimingModel;
import org.quartz.Trigger;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* @author Xiaohan.Yuan
* @version 1.0.0
* @ClassName TriggerManager.java
* @Description 触发器管理器, 用来生成触发器
* @createTime 2021年12月16日
*/
@Component
public class TriggerManager {
private final List triggerFactories;
public TriggerManager(List triggerFactories) {
this.triggerFactories = triggerFactories;
}
/**
* 生成对应的触发器
*
* @param timingModel 触发器model
* @return org.quartz.Trigger
* @author YuanXiaohan
* @date 2021/12/16 2:53 下午
*/
public Trigger build(TimingModel timingModel) {
for (ITriggerFactory triggerFactory : triggerFactories) {
if (triggerFactory.check(timingModel.getType())) {
return triggerFactory.build(timingModel);
}
}
return null;
}
}
该方法包含:
添加定时
更新定时触发器
更新任务参数
删除任务
暂停任务
将暂停的任务恢复执行
启动所有任务
关闭定时任务
获取所有任务
package org.demo.quartz;
import lombok.extern.slf4j.Slf4j;
import org.demo.quartz.mode.CronTimingModel;
import org.demo.quartz.mode.TimingModel;
import org.demo.quartz.trigger.TriggerManager;
import org.demo.quartz.exception.TimingException;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.context.annotation.Configuration;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
/**
@author Xiaohan.Yuan
@version 1.0.0
@ClassName QuartzTaskManager.java
@Description
@createTime 2021年12月16日
*/
@Configuration
@Slf4j
public class QuartzTaskManager {
private final Scheduler scheduler;
private final Boolean initStatus;
private final TriggerManager triggerManager;
private static QuartzTaskManager taskManager;
public QuartzTaskManager(Scheduler scheduler, TriggerManager triggerManager) {
this.scheduler = scheduler;
taskManager = this;
boolean status = true;
try {
// 启动调度器
scheduler.start();
} catch (SchedulerException e) {
log.error(“定时器调度器启动失败,定时器不可用!”, e);
status = false;
}
initStatus = status;
this.triggerManager = triggerManager;
}
public static QuartzTaskManager getInstance(){
return taskManager;
}
/**
添加定时任务
@param timingModel 任务model
@author YuanXiaohan
@date 2021/12/16 3:09 下午
*/
public void addTask(TimingModel timingModel) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, SchedulerException {
checkTimingInit();
// 构建任务信息
JobDetail jobDetail = JobBuilder.newJob(timingModel.getTaskClass().getDeclaredConstructor().newInstance().getClass())
.withDescription(timingModel.getDescription())
.withIdentity(timingModel.getTaskName(), timingModel.getGroupName())
.build();
// 构建触发器
Trigger trigger = triggerManager.build(timingModel);
// 将任务参数放入触发器中
if (timingModel.getParam() != null && !timingModel.getParam().isEmpty()) {
trigger.getJobDataMap().putAll(timingModel.getParam());
}
// 启动任务
scheduler.scheduleJob(jobDetail, trigger);
}
/**
更新任务,任务的标示(由taskName和groupName组成)不变,任务的触发器(触发频率)发生变化
@param timingModel 任务model
@author YuanXiaohan
@date 2021/12/16 3:15 下午
*/
public void updateTask(TimingModel timingModel) throws SchedulerException {
// 获取到任务
TriggerKey triggerKey = TriggerKey.triggerKey(timingModel.getTaskName(), timingModel.getGroupName());
// 构建触发器
Trigger trigger = triggerManager.build(timingModel);
// 将任务参数放入触发器中
if (timingModel.getParam() != null && !timingModel.getParam().isEmpty()) {
trigger.getJobDataMap().putAll(timingModel.getParam());
}
// 将新的触发器绑定到任务标示上重新执行
scheduler.rescheduleJob(triggerKey, trigger);
}
/**
更新任务参数
@param taskName 任务名
@param groupName 任务组名
@param param 参数
@author YuanXiaohan
@date 2021/12/16 3:20 下午
*/
public void updateTask(String taskName, String groupName, Map
// 获取到任务
TriggerKey triggerKey = TriggerKey.triggerKey(taskName, groupName);
Trigger trigger = scheduler.getTrigger(triggerKey);
//修改参数
trigger.getJobDataMap().putAll(param);
// 将新的触发器绑定到任务标示上重新执行
scheduler.rescheduleJob(triggerKey, trigger);
}
/**
/**
/**
/**
/**
/**
/**
}
package org.demo.quartz.config;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.stereotype.Component;
/**
* @author Xiaohan.Yuan
* @version 1.0.0
* @ClassName TaskJobFactory.java
* @Description 将Quartz注入springboot
* @createTime 2021年12月16日
*/
@Component
public class TaskJobFactory extends AdaptableJobFactory {
private final AutowireCapableBeanFactory capableBeanFactory;
public TaskJobFactory(AutowireCapableBeanFactory capableBeanFactory) {
this.capableBeanFactory = capableBeanFactory;
}
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
//调用父类的方法
Object jobInstance = super.createJobInstance(bundle);
//进行注入
capableBeanFactory.autowireBean(jobInstance);
return jobInstance;
}
}
package org.demo;
import org.demo.quartz.QuartzTaskManager;
import org.demo.quartz.mode.CronTimingModel;
import org.demo.quartz.mode.IntervalTimingMode;
import org.demo.quartz.mode.TriggerType;
import org.demo.quartz.task.TestQuartz;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.util.HashMap;
@SpringBootApplication
public class QuartzApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(QuartzApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
//构建CRON定时
//CronTimingModel cronTimingModel = new CronTimingModel(TestQuartz.class, "测试名", "测试组", "测试描述", "*/1 * * * * ?");
// 构建固定间隔定时
IntervalTimingMode intervalTimingMode = new IntervalTimingMode(TestQuartz.class, "测试名", "测试组", "测试描述", TriggerType.INTERVAL_SECOND, 5L,null);
HashMap param = new HashMap<>();
param.put("paramKey","获取到参数了");
intervalTimingMode.setParam(param);
QuartzTaskManager.getInstance().addTask(intervalTimingMode);
}
}
. ____ _ __ _ _
/\ / ___'_ __ _ _(_)_ __ __ _
( ( )___ | '_ | '| | ’ / ` |
/ )| |)| | | | | || (| | ) ) ) )
’ || .__|| ||| |, | / / / /
=========||==============|/=///_/
:: Spring Boot :: (v2.6.1)
2021-12-16 18:46:55.763 INFO 46460 --- [ main] org.demo.QuartzApplication : Starting QuartzApplication using Java 11.0.11 on xiaohandeiMac.local with PID 46460 (/Users/xiaohan/IdeaProjects/demo-quartz/target/classes started by xiaohan in /Users/xiaohan/IdeaProjects/demo-quartz)
2021-12-16 18:46:55.764 INFO 46460 --- [ main] org.demo.QuartzApplication : No active profile set, falling back to default profiles: default
2021-12-16 18:46:56.089 INFO 46460 --- [ main] org.quartz.impl.StdSchedulerFactory : Using default implementation for ThreadExecutor
2021-12-16 18:46:56.095 INFO 46460 --- [ main] org.quartz.core.SchedulerSignalerImpl : Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
2021-12-16 18:46:56.095 INFO 46460 --- [ main] org.quartz.core.QuartzScheduler : Quartz Scheduler v.2.3.2 created.
2021-12-16 18:46:56.096 INFO 46460 --- [ main] org.quartz.simpl.RAMJobStore : RAMJobStore initialized.
2021-12-16 18:46:56.096 INFO 46460 --- [ main] org.quartz.core.QuartzScheduler : Scheduler meta-data: Quartz Scheduler (v2.3.2) 'quartzScheduler' with instanceId 'NON_CLUSTERED'
Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
NOT STARTED.
Currently in standby mode.
Number of jobs executed: 0
Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 10 threads.
Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered.
2021-12-16 18:46:56.096 INFO 46460 --- [ main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler 'quartzScheduler' initialized from an externally provided properties instance.
2021-12-16 18:46:56.096 INFO 46460 --- [ main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler version: 2.3.2
2021-12-16 18:46:56.096 INFO 46460 --- [ main] org.quartz.core.QuartzScheduler : JobFactory set to: org.springframework.scheduling.quartz.SpringBeanJobFactory@2d6aca33
2021-12-16 18:46:56.099 INFO 46460 --- [ main] org.quartz.core.QuartzScheduler : Scheduler quartzScheduler_$_NON_CLUSTERED started.
2021-12-16 18:46:56.147 INFO 46460 --- [ main] org.demo.QuartzApplication : Started QuartzApplication in 0.589 seconds (JVM running for 6.058)
1
2021-12-16 18:46:56.156 INFO 46460 --- [eduler_Worker-1] org.demo.quartz.task.TestQuartz : ----------------------
任务组:测试名
任务名:测试组
任务描述:测试描述
获取参数paramKey:获取到参数了
----------------------
2
2021-12-16 18:47:01.155 INFO 46460 --- [eduler_Worker-2] org.demo.quartz.task.TestQuartz : ----------------------
任务组:测试名
任务名:测试组
任务描述:测试描述
获取参数paramKey:获取到参数了
----------------------
3
2021-12-16 18:47:06.151 INFO 46460 --- [eduler_Worker-3] org.demo.quartz.task.TestQuartz : ----------------------
任务组:测试名
任务名:测试组
任务描述:测试描述
获取参数paramKey:获取到参数了
----------------------
Gitee