所使用的jar包
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-quartzartifactId>
dependency>
使用默认单机模式。单机模式中,Job 和Trigger是存放在内存中Map,通过源码可以看出 quartz-2.3.0.sources.jar!/org/quartz/simpl/RAMJobStore.java
protected HashMap<JobKey, JobWrapper> jobsByKey = new HashMap<JobKey, JobWrapper>(1000);
protected HashMap<TriggerKey, TriggerWrapper> triggersByKey = new HashMap<TriggerKey, TriggerWrapper>(1000);
protected HashMap<String, HashMap<JobKey, JobWrapper>> jobsByGroup = new HashMap<String, HashMap<JobKey, JobWrapper>>(25);
protected HashMap<String, HashMap<TriggerKey, TriggerWrapper>> triggersByGroup = new HashMap<String, HashMap<TriggerKey, TriggerWrapper>>(25);
protected TreeSet<TriggerWrapper> timeTriggers = new TreeSet<TriggerWrapper>(new TriggerWrapperComparator());
protected HashMap<String, Calendar> calendarsByName = new HashMap<String, Calendar>(25);
protected Map<JobKey, List<TriggerWrapper>> triggersByJob = new HashMap<JobKey, List<TriggerWrapper>>(1000);
protected final Object lock = new Object();
protected HashSet<String> pausedTriggerGroups = new HashSet<String>();
protected HashSet<String> pausedJobGroups = new HashSet<String>();
protected HashSet<JobKey> blockedJobs = new HashSet<JobKey>();
官网支持集群是把这些数据存放在mysql, 也有人改成使用redis存放这些数据
注意此处Job要扩展QuartzJobBean, 只有这样才能使用@Autowired进来的其它service实例,否则要显式地new 一个相应service的实例
@Slf4j
@Component
@DisallowConcurrentExecution
public class MyJob extends QuartzJobBean {
@Autowired
ApplicationService applicationService;
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
log.info("触发MyJob=========");
applicationService.doSomething();
Trigger trigger = jobExecutionContext.getTrigger();
log.info("触发MyJob==========the trigger time : {}, 当前时间: {}",
trigger.getStartTime(), new Date());
}
}
设置Schedule
public interface QuartzService {
void startSchedule ();
void deployMySchedule (Myparams params) throws SchedulerException, ParseException;
}
定义trigger并设置Schedule
@Slf4j
@Service
public class QuartzServiceImpl implements QuartzService {
private final SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss", Locale.ENGLISH);
private final String myJobName = "MyJobName";
private final String QUARTZ_JOB_GROUP_SUFFIX = "Group";
@Autowired
private Scheduler scheduler;
@Override
public void deployMySchedule (MyParams params) throws SchedulerException, ParseException {
if (checkMyJobExists(params)) {
log.warn("已存在{}的任务", params.getJobName());
updateMyJobTrigger(params);
} else {
log.info("不存在{}的任务, 添加任务", params.getJobName());
arrangeNewMyJobSchedule(params);
}
}
@Override
public void startSchedule () {
try {
scheduler.start();
} catch (SchedulerException e) {
log.error("Quartz 启动Schedule 出现异常 ==== ", e);
}
}
/**
* {
* "myJobName": "my.customized.job.name",
* "triggerTime": "2023-09-21 23:23:23"
* }
* @param params
* @throws SchedulerException
*/
private void arrangeNewMyJobSchedule (MyParams params) throws SchedulerException, ParseException {
String jobName = params.getMyJobName();
String jobGroup = params.getMyJobName()+QUARTZ_JOB_GROUP_SUFFIX;
String triggerTime = params.getTriggerTime();
// TODO change triggerTime to Date()
Date date = formatter.parse(triggerTime);
log.info("{} 添加定时任务", params.getMyJobName());
JobKey theJobKey = jobKey(jobName, jobGroup);
JobDetail job = JobBuilder.newJob(MyJob.class)
.usingJobData("myJobName", params.getMyJobName())
.withIdentity(theJobKey)
.build();
// Simple trigger without repeating
// withMisfireHandlingInstructionNextWithRemainingCount()
// Does nothing, misfired execution is ignored and there is no next execution.
// Use this instruction when you want to completely discard the misfired execution.
// Example scenario: the trigger was suppose to start recording of a program in TV.
// There is no point of starting recording when the trigger misfired and is already 2 hours late.
// Discarded but job not removed
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity(triggerKey(jobName, jobGroup))
.startAt(date)
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withMisfireHandlingInstructionNextWithRemainingCount())
.build();
// 使用触发器调度任务的执行
scheduler.scheduleJob(job, trigger);
log.info("{} 设置任务触发时间为: {}, 配置触发器", params.getMyJobName(), triggerTime);
log.info("schedule: scheduleName {}, scheduleInstanceId {} ", scheduler.getSchedulerName(),
scheduler.getSchedulerInstanceId());
}
private void updateMyJobTrigger (MyParams params) throws SchedulerException, ParseException {
String jobName = params.getMyJobName();
String jobGroup = params.getMyJobName()+"Group";
String triggerTime = params.getTriggerTime();
updateJobTrigger(jobName, jobGroup, triggerTime);
}
private boolean checkMyJobExists (MyParams params) throws SchedulerException {
String jobName = params.getMyJobName();
String jobGroup = params.getMyJobName()+QUARTZ_JOB_GROUP_SUFFIX;
JobKey jobKey = new JobKey(jobName, jobGroup);
return scheduler.checkExists(jobKey);
}
private void updateJobTrigger (String jobName, String jobGroup, String triggerTime) throws ParseException {
// TODO change triggerTime to Date()
Date date = formatter.parse(triggerTime);
try {
TriggerKey triggerKey = triggerKey(jobName, jobGroup);
SimpleTrigger oldTrigger = (SimpleTrigger) scheduler.getTrigger(triggerKey);
// Simple trigger without repeating
// withMisfireHandlingInstructionNextWithRemainingCount()
// 如果给的时间小于当前时间, 只重新配置触发器, 并不触发, 同时 jobdetail 也没有删除
Trigger newTrigger = oldTrigger.getTriggerBuilder().withIdentity(triggerKey)
.withSchedule(simpleSchedule()
.withMisfireHandlingInstructionNextWithRemainingCount())
.startAt(date)
.build();
// 重启触发器
scheduler.rescheduleJob(oldTrigger.getKey(), newTrigger);
log.info("Job {} 任务触发时间更新为: {}, 重新配置触发器", jobName, triggerTime);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}
在项目启动时加载schedule
@Slf4j
@Component
public class MyQuartzScheduleStart {
@Autowired
QuartzService quartzService;
@PostConstruct
public void init() {
log.info("Quartz 调度任务开始 =====");
quartzService.startSchedule();
}
}
扩展阅读:线程池,多schedule, 如果Job里 Autowire了其它的 Bean,应该怎么办,官方的配置说明,官方教程,Quartz Scheduler Misfire Instructions Explained, Misfire, getting-quartz-net-to-ignore-misfires, using-disallowconcurrentexecution-in-quartz-scheduler, Quartz + Redis实现集群定时任务
例子:spring-boot-quartz-scheduler-email-scheduling-example, Quartz_Scheduler_Example_Programs_and_Sample_Code, SpringBoot——Quartz定时框架的使用详解和总结
详细解说:Quartz 是什么?一文带你入坑