功能点Quartz 是一个完全由 Java 编写的开源作业调度框架,为在 Java 应用程序中进行作业调度提供了简单却强大的机制。该文章不过多介绍Quartz的概念,主要做一个封装的记录。
- 添加CRON、固定间隔定时
- 修改定时的触发器
- 修改定时参数
- 暂停定时
- 启动暂停的定时
- 获取所有定时
- 启动所有定时
- 停止定时
创建一个简单的SpringBoot项目,pom如下:
1. 定时类型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和固定间隔两种定时类型,后期需要支持跳过节假日、周一~周五、周末等需求,所以需要让定时类型支持拓展。
1.1定时类型package org.demo.quartz.mode;
public enum TriggerType {
CRON("标准CRON支持"),
INTERVAL_MILLISECOND("固定间隔毫秒"),
INTERVAL_SECOND("固定间隔秒"),
INTERVAL_MINUTE("固定间隔分钟"),
INTERVAL_HOUR("固定间隔小时"),
WEEKDAYS("工作日,跳过节假日"),
HOLIDAY("节假日")
;
private String describe;
TriggerType(String describe) {
this.describe = describe;
}
}
1.2 构建定时任务的抽象类
我们需要构建不同的定时类型,不同的定时类型需要的参数也是不同的,因此我们需要抽象出定时的公用参数,将不同的参数多态实现。
package org.demo.quartz.mode;
import lombok.Getter;
import lombok.Setter;
import org.demo.quartz.task.QuartzTaskJob;
import java.util.Map;
@Getter
@Setter
public class TimingModel {
private Class extends QuartzTaskJob> taskClass;
private String taskName;
private String groupName;
private String description;
private TriggerType type;
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;
}
}
1.3 用以构建CRON定时任务
package org.demo.quartz.mode;
import lombok.Getter;
import lombok.Setter;
import org.demo.quartz.task.QuartzTaskJob;
import java.util.Map;
@Getter
@Setter
public class CronTimingModel extends TimingModel{
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;
}
}
1.4 用以构建固定间隔定时任务
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;
@Getter
@Setter
public class IntervalTimingMode extends TimingModel {
private Long interval;
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;
}
}
2.抽象任务类
package org.demo.quartz.task;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public interface QuartzTaskJob extends Job {
@Override
void execute(JobExecutionContext context) throws JobExecutionException;
}
2.1 实现一个测试任务
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("----------------------" +
"n任务组:{}n任务名:{}n任务描述:{}n获取参数paramKey:{}n" +
"----------------------"
,name,group,description,jobDataMap.getString("paramKey"));
try {
// QuartzJobManager.getInstance().jobdelete(this.getClass().getSimpleName(),"ah");//执行完此任务就删除自己
} catch (Exception e) {
e.printStackTrace();
}
}
}
3. 构建触发器的不同实现
3.1 抽象触发器实现
package org.demo.quartz.trigger;
import org.demo.quartz.mode.TriggerType;
import org.demo.quartz.mode.TimingModel;
import org.quartz.Trigger;
public interface ITriggerFactory {
public boolean check(TriggerType triggerType);
public Trigger build(TimingModel timingModel);
}
3.2 CRON触发器
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;
@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();
}
}
3.3 固定间隔触发器
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;
@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();
}
}
3.4 构建触发器工厂
package org.demo.quartz.trigger;
import org.demo.quartz.mode.TimingModel;
import org.quartz.Trigger;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class TriggerManager {
private final List triggerFactories;
public TriggerManager(List triggerFactories) {
this.triggerFactories = triggerFactories;
}
public Trigger build(TimingModel timingModel) {
for (ITriggerFactory triggerFactory : triggerFactories) {
if (triggerFactory.check(timingModel.getType())) {
return triggerFactory.build(timingModel);
}
}
return null;
}
}
3. 构建定时管理类
该方法包含:
- 添加定时
- 更新定时触发器
- 更新任务参数
- 删除任务
- 暂停任务
- 将暂停的任务恢复执行
- 启动所有任务
- 关闭定时任务
- 获取所有任务
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.*;
@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;
}
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);
}
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);
}
public void updateTask(String taskName, String groupName, Map param) throws SchedulerException {
// 获取到任务
TriggerKey triggerKey = TriggerKey.triggerKey(taskName, groupName);
Trigger trigger = scheduler.getTrigger(triggerKey);
//修改参数
trigger.getJobDataMap().putAll(param);
// 将新的触发器绑定到任务标示上重新执行
scheduler.rescheduleJob(triggerKey, trigger);
}
public void deleteTask(String taskName, String groupName) throws SchedulerException {
// 暂停任务对应的触发器
scheduler.pauseTrigger(TriggerKey.triggerKey(taskName, groupName));
// 删除任务对应的触发器
scheduler.unscheduleJob(TriggerKey.triggerKey(taskName, groupName));
// 删除任务
scheduler.deleteJob(JobKey.jobKey(taskName, groupName));
}
public void pauseTask(String taskName, String groupName) throws SchedulerException {
scheduler.pauseJob(JobKey.jobKey(taskName, groupName));
}
public void resumetask(String taskName, String groupName) throws SchedulerException {
scheduler.resumeJob(JobKey.jobKey(taskName, groupName));
}
public void startAllTasks() {
try {
scheduler.start();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void shutdownAllTasks() {
try {
if (!scheduler.isShutdown()) {
scheduler.shutdown();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public List getTaskList() throws SchedulerException {
GroupMatcher matcher = GroupMatcher.anyJobGroup();
Set jobKeys = scheduler.getJobKeys(matcher);
List taskList = new ArrayList<>();
for (JobKey jobKey : jobKeys) {
List extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
for (Trigger trigger : triggers) {
TimingModel timingModel;
if (trigger instanceof CronTrigger) {
timingModel = new CronTimingModel(null, jobKey.getName(), jobKey.getGroup(), null, ((CronTrigger) trigger).getCronexpression());
timingModel.setTaskStatus(scheduler.getTriggerState(trigger.getKey()).name());
taskList.add(timingModel);
} else {
log.warn("name:{},group:{}的定时任务类型未知,请拓展QuartzTaskManager.getTaskList的任务类型解析", jobKey.getName(), jobKey.getGroup());
}
}
}
return taskList;
}
private void checkTimingInit() {
if (!initStatus) {
throw new TimingException("定时器未初始化,添加定时器失败!");
}
}
}
4. Quartz注入到SpringBoot
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;
@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;
}
}
5. 执行
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);
}
}
5.1 执行结果
. ____ _ __ _ _ /\ / ___'_ __ _ _(_)_ __ __ _ ( ( )___ | '_ | '_| | '_ / _` | \/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |___, | / / / / =========|_|==============|___/=/_/_/_/ :: 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



