一、本文主要内容
Quartz是一款开源的定时任务调度框架,本文主要记录一下在工作中使用springboot整合quartz实现定时任务调度管理的用例。内容主要有:springboot整合quartz相关配置、实现基于simpleTrigger的定时任务、实现基于cronTrigger的定时任务。
二、代码案例
0、环境准备
@SpringBootApplication
public class QuartzApplication {
public static void main(String[] args){
SpringApplication.run(QuartzApplication.class,args);
}
/**
* 初始注入scheduler
* @return
* @throws SchedulerException
*/
@Bean
public Scheduler scheduler() throws SchedulerException {
SchedulerFactory schedulerFactoryBean = new StdSchedulerFactory();
return schedulerFactoryBean.getScheduler();
}
}
/**
* @Author HanGuangKai
* @Date 2021/7/25 18:55
* @description 定时任务
*/
public class MyJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
String nowTime = format.format(new Date());
System.out.println("定时任务被触发了"+nowTime);
}
}
1、开启一个任务
/**
* @Author HanGuangKai
* @Date 2021/7/25 18:58
* @description quartz配置类
*/
@Configuration
public class QuartzConfig {
@Resource
private Scheduler scheduler;
/**
* 01-开启任务
*/
public void startJob() {
openJob(scheduler);
// 启动任务
try {
scheduler.start();
} catch (SchedulerException e) {
e.printStackTrace();
}
}
/**
* 开启一个任务
* @param scheduler
*/
private void openJob(Scheduler scheduler) {
// 1.创建一个JobDetail
JobDetail build = JobBuilder.newJob(MyJob.class).withIdentity("job1", "group1").build();
// 2.触发器表达式对象
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/4 * * * * ?");
// 3.准备一个出触发器对象
CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggergroup1")
.withSchedule(cronScheduleBuilder).build();
// 4.开始调度
try {
scheduler.scheduleJob(build, cronTrigger);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}
@RestController
@RequestMapping(value = "/quartz")
public class QuartzController {
// 任务调度器
@Autowired
private QuartzConfig quartzConfig;
/**
* 开启一个定时任务
*/
@RequestMapping(value = "open-job")
public String openJob() {
quartzConfig.startJob();
return "定时任务开启成功!";
}
}
2、暂停一个任务
@Configuration
public class QuartzConfig {
@Resource
private Scheduler scheduler;
/**
* 暂停一个任务
* @param name 任务名
* @param group 任务组名
*/
public void pauseJob(String name,String group) throws SchedulerException {
// 任务的标识类
JobKey jobKey = new JobKey(name, group);
// 获取此任务
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
if (jobDetail != null) {
// 暂停此任务
scheduler.pauseJob(jobKey);
} else {
System.out.println("无此定时任务!");
}
}
}
public class QuartzController {
// 任务调度器
@Autowired
private QuartzConfig quartzConfig;
@RequestMapping(value = "pause-job")
public String pauseJob(@RequestParam("name") String name, @RequestParam("group") String group) {
try {
quartzConfig.pauseJob(name, group);
} catch (SchedulerException e) {
e.printStackTrace();
return "任务暂停异常!";
}
return "任务暂停成功!";
}
}
3、打印任务的基本信息
/**
* @Author HanGuangKai
* @Date 2021/7/25 19:49
* @description 任务对象
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TaskInfo {
private String jobName;
private String jobGroup;
private String jobDescription;
private String status;
private String cronExpression;
private String cronDescription;
}
/**
* 查看所有的任务信息
* @return
*/
public List<TaskInfo> getAllJobsInfo() throws SchedulerException {
ArrayList<TaskInfo> taskInfos = new ArrayList<>();
// 所有任务组
List<String> jobGroupNames = scheduler.getJobGroupNames();
for (String jobGroupName : jobGroupNames) {
Set<JobKey> jobKeys = scheduler.getJobKeys(GroupMatcher.<JobKey>groupEquals(jobGroupName));
for (JobKey jobKey : jobKeys) {
List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
for (Trigger trigger : triggers) {
Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
String cronException = ""; // cron表达式
String cronDescription = ""; // 描述信息
if (trigger instanceof CronTrigger) {
CronTrigger cronTrigger = (CronTrigger)trigger;
// cron表达式
cronException = cronTrigger.getCronExpression();
cronDescription = cronTrigger.getDescription();
}
TaskInfo taskInfo = new TaskInfo();
taskInfo.setJobName(jobKey.getName());
taskInfo.setJobGroup(jobKey.getGroup());
taskInfo.setJobDescription(jobDetail.getDescription());
taskInfo.setStatus(triggerState.name());
taskInfo.setCronExpression(cronException);
taskInfo.setJobDescription(cronDescription);
taskInfos.add(taskInfo);
}
}
}
@RequestMapping(value = "get-all-job")
public List<TaskInfo> getAllTasks() {
List<TaskInfo> allJobsInfo = null;
try {
allJobsInfo = quartzConfig.getAllJobsInfo();
} catch (SchedulerException e) {
e.printStackTrace();
return null;
}
return allJobsInfo;
}
4、关于暂停和恢复任务
/**
* 恢复某个定时任务的执行
* @param name
* @param group
*/
public void resumeJob(String name,String group) throws SchedulerException {
JobKey jobKey = new JobKey(name, group);
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
if (jobDetail != null) {
scheduler.resumeJob(jobKey);
} else {
System.out.println("要恢复的任务不存在");
}
}
@RequestMapping(value = "resume-job")
public String resumeJob(@RequestParam("name") String name, @RequestParam("group") String group) {
try {
quartzConfig.resumeJob(name, group);
} catch (SchedulerException e) {
e.printStackTrace();
return "任务恢复异常!";
}
return "任务恢复成功!";
}
当暂停一个定时任务后,再将其恢复,会发现控制台输出很多遍相同内容:(即把之前暂停应该打印的内容,在恢复后重新打印了一遍)
定时任务被触发了2021-07-25 08:23:28
定时任务被触发了2021-07-25 08:23:42
定时任务被触发了2021-07-25 08:23:42
定时任务被触发了2021-07-25 08:23:42
定时任务被触发了2021-07-25 08:23:44
5、删除任务
/**
* 删除某个定时任务
* @param name
* @param group
*/
public void deleteJob(String name,String group) throws SchedulerException {
JobKey jobKey = new JobKey(name, group);
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
if (jobDetail != null) {
scheduler.deleteJob(jobKey);
} else {
System.out.println("要删除的任务不存在");
}
}
@RequestMapping(value = "delete-job")
public String deleteJob(@RequestParam("name") String name, @RequestParam("group") String group) {
try {
quartzConfig.deleteJob(name, group);
} catch (SchedulerException e) {
e.printStackTrace();
return "任务删除异常!";
}
return "任务删除成功!";
}
6、动态修改一个定时任务
/**
* 动态修改一个定时任务的cron表达式(即触发规则)
*/
public boolean modifyJob(String name,String group,String newTime) throws SchedulerException {
Date date = null;
TriggerKey triggerKey = new TriggerKey(name, group);
Trigger trigger = scheduler.getTrigger(triggerKey);
CronTrigger cronTrigger = null;
if (trigger instanceof CronTrigger) {
cronTrigger = (CronTrigger) trigger;
// 获取到表达式
String cronExpression = cronTrigger.getCronExpression();
if (cronExpression.equalsIgnoreCase(newTime)) {
System.out.println("需要修改原来的表达式:"+cronExpression+"为"+newTime);
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(newTime);
// 新的cronTrigger1
CronTrigger cronTrigger1 = TriggerBuilder.newTrigger().withIdentity(name, group).withSchedule(cronScheduleBuilder).build();
date = scheduler.rescheduleJob(triggerKey, cronTrigger1);
} else {
System.out.println("二者一样,不用修改");
}
}
if (date != null) {
return true;
} else {
return false;
}
@RequestMapping("modify-job")
public boolean modifyJob(@RequestParam("name") String name, @RequestParam("group") String group,@RequestParam("cron") String cron) {
boolean b = false;
try {
b = quartzConfig.modifyJob(name, group, cron);
} catch (SchedulerException e) {
e.printStackTrace();
return b;
}
return b;
}
7、web项目开启时便开启定时任务
package com.hgk.quartz.listener;
import com.hgk.quartz.config.QuartzConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextRefreshedEvent;
/**
* @Author HanGuangKai
* @Date 2021/7/26 20:07
* @description 自定义一个监听器监听web启动
*/
@Configuration
public class QuartzListener implements ApplicationListener<ContextRefreshedEvent> {
@Autowired
private QuartzConfig quartzConfig;
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
quartzConfig.startJob();
System.out.println("web项目启动了!");
}
}
8、如何在JOB任务中获取Spring容器中的对象-为了调用方法
/**
* 开启一个任务
* @param scheduler
*/
private void openJob(Scheduler scheduler) {
// 将spring上下文对象放到容器中
JobDataMap map = new JobDataMap();
map.put("applicationContext",applicationContext);
// 然后创建对象的时候传过去
// 1.创建一个JobDetail
JobDetail build = JobBuilder.newJob(MyJob.class).usingJobData(map)
.withIdentity("job1", "group1").build();
// 2.触发器表达式对象
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/4 * * * * ?");
// 3.准备一个出触发器对象
CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggergroup1")
.withSchedule(cronScheduleBuilder).build();
// 4.开始调度
try {
scheduler.scheduleJob(build, cronTrigger);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
public class MyJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
ApplicationContext applicationContext1 = (ApplicationContext) context.getJobDetail().getJobDataMap().get("applicationContext");
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
String nowTime = format.format(new Date());
System.out.println("定时任务被触发了"+nowTime);
// 现在就可以调用业务逻辑层方法了(从spring容器中获取对象)
OrderService orderService = applicationContext1.getBean(OrderService.class);
orderService.sayHi();
}
}
9、关于quartz的暂停补偿问题
/**
* 开启一个任务
* @param scheduler
*/
private void openJob(Scheduler scheduler) {
// 将spring上下文对象放到容器中
JobDataMap map = new JobDataMap();
map.put("applicationContext",applicationContext);
// 然后创建对象的时候传过去
// 1.创建一个JobDetail
JobDetail build = JobBuilder.newJob(MyJob.class).usingJobData(map)
.withIdentity("job1", "group1").build();
// 2.触发器表达式对象
/**
* withMisfireHandlingInstructionDoNothing():不触发立即执行,一个任务恢复执行,以前错过的定时任务不补偿,继续执行下一个周期
* withMisfireHandlingInstructionFireAndProceed():以当前时间先触发一次,以后继续按照cron执行
* withMisfireHandlingInstructionIgnoreMisfires():举例:如果九点暂停定时任务,十点十五恢复定时任务,错误的九点十点的定时任务补偿,以后正常执行
*/
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/4 * * * * ?").withMisfireHandlingInstructionDoNothing();
// 3.准备一个出触发器对象
CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggergroup1")
.withSchedule(cronScheduleBuilder).build();
// 4.开始调度
try {
scheduler.scheduleJob(build, cronTrigger);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
注意:还要写配置文件withMisfireHandlingInstructionDoNothing才能生效。