springboot 动态定时任务
springboot实现动态定时任务的方法有两种:
- 可以实现
SchedulingConfigurer
接口(可以见springboot 动态定时任务)- 整合quartz(当前文章要说的)
springboot整合需要的依赖
springboot整合quartz需要导入的依赖如下
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
在类路径下新增配置文件
quartz.properties
# scheduler名称
org.quartz.scheduler.instanceName = MyScheduler
# 线程池初始化数
org.quartz.threadPool.threadCount = 10
# 持久化规则,这里是持久化到内存,官方文档上有持久化到数据库的
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
或者在yml文件新增配置
spring:
quartz:
job-store-type: MEMORY
schedulerName: MyScheduler
quartz相关的介绍
quartz中最重要的就是
Job
和Trigger
,可以理解为任务和触发器,任务------>触发器是一对多的关系,一个任务可以有多个触发器,触发器------>任务是一对一的关系,一个触发器只能对应一个任务
Job的构建
springboot中job构建的方式是继承QuartzJobBean类,QuartzJobBean类是springboot封装过的,也可以直接去实现job接口,job接口是quartz的,推荐是使用继承QuartzJobBean,代码如下
@Slf4j
public class ReportAutoJob extends QuartzJobBean {
private String id;
public void setId(String id) {
this.id = id;
}
/**
* 调度器执行的方法
* @param jobExecutionContext
* @throws JobExecutionException
*/
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
log.info("自动任务开始执行");
}
}
Trigger相关的构建
直接使用TriggerBuilder类来构建 首先介绍触发器规则构建类 一共有四种:
- CalendarIntervalScheduleBuilder(相当于SimpleScheduleBuilder的升级版多支持了天周年月的间隔执行)
- CronScheduleBuilder(可以构建cron表达式)
- DailyTimeIntervalScheduleBuilder(支持复杂的构建 例如OnMondayThroughFriday方法从周一到周五,推荐自己测试一下)
- SimpleScheduleBuilder(简单的构建器支持配置执行次数,支持时分秒为单位的间隔执行)
// 这里是间隔一分钟循环执行
SimpleScheduleBuilder simpleScheduleBuilder = SimpleScheduleBuilder.repeatMinutelyForever(1);
Trigger trigger = TriggerBuilder.newTrigger()
// trigger名称和trigger分组名
.withIdentity( "triggerListener", "triggerListenerGroup")
.withSchedule(simpleScheduleBuilder)
.build();
JobDetail相关的构建
JobDetail相关的构建
// 创建JobDetail
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("reportAutoService", this);
jobDataMap.put("frequencyType", frequencyType);
JobDetail myJob = JobBuilder.newJob(ReportAutoJob.class)
// 设置相关的参数值,在任务中可以直接获取到
.usingJobData("id", id)
.usingJobData(jobDataMap)
.withIdentity(jobName + "job", jobName + "jobGroup")
.build();
JobDataMap的介绍
JobDataMap可以给任务传参在job中可以获取到,一共有两种方式:
- set方法
- jobExecutionContext
public class ReportAutoJob extends QuartzJobBean {
private String id;
/**
* 1.Quartz自动赋值
* @param id
*/
public void setId(String id) {
this.id = id;
}
/**
* 调度器执行的方法
* @param jobExecutionContext
* @throws JobExecutionException
*/
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
log.info("自动任务开始执行 id: {}",id);
// 2.直接获取
JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();
ReportAutoService reportAutoService = (ReportAutoService);
}
}
组合Trigger和Job构成一个完整的定时任务
使用SimpleScheduleBuilder构建一个完整定时任务
public void createListener(){
// 规则构建器
SimpleScheduleBuilder simpleScheduleBuilder = SimpleScheduleBuilder.repeatMinutelyForever(1);
Trigger trigger = TriggerBuilder.newTrigger()
// trigger名称和trigger分组名
.withIdentity( "triggerListener", "triggerListenerGroup")
.withSchedule(simpleScheduleBuilder)
.build();
JobDetail myJobListener = JobBuilder.newJob(ReportTaskListening.class)
// JobDetail 名称和JobDetail分组名
.withIdentity("jobListener", "jobListenerGroup")
.build();
Scheduler scheduler = null;
try {
// 获取默认的Scheduler(任务调度器)
scheduler = StdSchedulerFactory.getDefaultScheduler();
// 组合JobDetail和Trigger构成一个完整的定时任务
scheduler.scheduleJob(myJobListener, trigger);
// 将所有处于待机的任务置于开始状态(这里的开始状态不是立即执行任务,而是到达任务触发器指定的触发时间才会执行)
scheduler.start();
} catch (SchedulerException e) {
e.printStackTrace();
}
}
这里是我开发时写的创建方法可以参考
/**
* 创建指定的定时任务
* @param cron cron表达式
* @param triggerName 调度器名称
* @param jobName 任务名称
* @param id 任务的id这里最好自动生成
* @param frequencyType 任务的类型(按天,按月,按周)
* @param frequencyDay 按天传的参数,隔多少天执行一次
* @param startTime 任务的开始时间
*/
public void creteScheduler(String cron, String triggerName, String jobName, String id, Integer frequencyType,
Integer frequencyDay, Date startTime) {
Trigger trigger = null;
Trigger triggerOne = null;
// 如果是按天
if (frequencyType.equals(ReportEnums.AutoEnums.DAY.getKey())) {
// 直接使用简单的构建器使用CalendarIntervalScheduleBuilder也一样的
SimpleScheduleBuilder simpleScheduleBuilder = SimpleScheduleBuilder.repeatHourlyForever(24 * frequencyDay);
trigger = TriggerBuilder.newTrigger()
// 设置任务开始时间
.startAt(startTime)
.withIdentity(triggerName + "trigger", triggerName + "triggerGroup")
.withSchedule(simpleScheduleBuilder)
.build();
} else {
// 不是按天直接用cron表达式
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
trigger = TriggerBuilder.newTrigger()
.startNow()
.withIdentity(triggerName + "trigger", triggerName + "triggerGroup")
.withSchedule(cronScheduleBuilder)
.build();
if (System.currentTimeMillis() < startTime.getTime()) {
triggerOne = TriggerBuilder.newTrigger()
// 设置任务开始时间
.startAt(startTime)
// 设置任务结束时间
.endAt(startTime)
// 可以直接在调度器上直接指定任务是那一个,参数分别是任务名称和任务分组名称
.forJob(jobName + "job", jobName + "jobGroup")
.withIdentity(triggerName + "oneTrigger")
.build();
}
}
// 创建JobDetail
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("reportAutoService", this);
jobDataMap.put("frequencyType", frequencyType);
JobDetail myJob = JobBuilder.newJob(ReportAutoJob.class)
// 设置相关的参数值,在任务中可以直接获取到
.usingJobData("id", id)
.usingJobData(jobDataMap)
.withIdentity(jobName + "job", jobName + "jobGroup")
.build();
try {
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// 1.注意第一次构建定时任务可以scheduleJob,但如果构架过了再使用会出现异常就是JobDetail已经注册过了
scheduler.scheduleJob(myJob, trigger);
// 不是按天执行
if (!frequencyType.equals(ReportEnums.AutoEnums.DAY.getKey())&&System.currentTimeMillis() < startTime.getTime()) {
scheduler.scheduleJob(triggerOne);
}
} catch (SchedulerException e) {
e.printStackTrace();
}
}
任务的恢复暂停和删除
/**
* 删除指定的任务
* @param jobName
* @param jobGroupName
*/
@Override
public void deleteJob(String jobName, String jobGroupName) {
try {
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
scheduler.deleteJob(jobKey);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 修改 一个job的 时间表达式
*
* @param triggerName
* @param triggerGroupName
* @param jobTime
*/
@Override
public void updateJob(String triggerName, String triggerGroupName, String jobTime) {
try {
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
.withSchedule(CronScheduleBuilder.cronSchedule(jobTime)).build();
// 重启触发器
scheduler.rescheduleJob(triggerKey, trigger);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
/**
* 暂停一个job
*
* @param jobName
* @param jobGroupName
*/
@Override
public void pauseJob(String jobName, String jobGroupName) {
try {
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
scheduler.pauseJob(jobKey);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
/**
* 恢复一个job
*
* @param jobName
* @param jobGroupName
*/
@Override
public void resumeJob(String jobName, String jobGroupName) {
try {
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
scheduler.resumeJob(jobKey);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
获取定时任务列表,可以获取到定时任务相关的信息
public void getTasks() {
List<Map<String, Object>> jobList = null;
try {
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
jobList = new ArrayList<Map<String, Object>>(16);
log.info("任务数量:{}",jobKeys.size()-1);
for (JobKey jobKey : jobKeys) {
List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
for (Trigger trigger : triggers) {
Map<String, Object> map = new HashMap<>(16);
map.put("jobName", jobKey.getName());
map.put("jobGroupName", jobKey.getGroup());
map.put("description", "触发器:" + trigger.getKey());
Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
map.put("jobStatus", triggerState.name());
if (trigger instanceof CronTrigger) {
CronTrigger cronTrigger = (CronTrigger) trigger;
String cronExpression = cronTrigger.getCronExpression();
CronExpression cron = new CronExpression(cronExpression);
Date now = new Date();
// 之前任务下次执行时间
String format = DateUtil.format(cron.getTimeAfter(now), "yyyy-MM-dd HH:mm:ss");
log.info("cron任务名称:{} 任务状态:{} 下次执行时间:{}",jobKey.getName().replace("job",""),triggerState.name(),format);
map.put("jobTime", cronExpression);
}
if (trigger instanceof SimpleTrigger) {
SimpleTrigger simpleTrigger = (SimpleTrigger) trigger;
if ("triggerListener".equals(simpleTrigger.getKey().getName())){
continue;
}
Date fireTimeAfter = simpleTrigger.getFireTimeAfter(new Date());
String format = DateUtil.format(fireTimeAfter, "yyyy-MM-dd HH:mm:ss");
log.info("simple任务名称:{} 任务状态:{} 下次执行时间:{}",jobKey.getName().replace("job",""),triggerState.name(),format);
}
jobList.add(map);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}