SpingBoot+Quartz实现多定时任务动态管理
业务需求:定时推送系统通知,系统通知可新增,修改,删除
文章参考自: https://blog.csdn.net/a510750/article/details/90241004
代码:
<!--引入quartz定时框架-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
MessageService类: 业务数据操作的Service
queryMessageCronList():查询所有未发布的系统通知
updateMessageStatus():修改系统通知状态为已发布
且在新增,修改,删除系统通知时调用QuartzScheduler类中对应的方法即可
由日期转cron的方法:
public String getCron(Date date){
//cron由左到右按顺序代表 : * * * * * * *
//格式: [秒] [分] [小时] [日] [月] [周] [年]
String dateFormat="ss mm HH dd MM ? yyyy";
SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
String formatTimeStr = null;
if (date != null) {
formatTimeStr = sdf.format(date);
}
return formatTimeStr;
}
QuartzScheduler类:涉及的是基本的配置,以后的动态修改方法
package com.ksxx.platform.auth.schedule;
import com.ksxx.message.auth.po.MessageCron;
import com.ksxx.platform.auth.service.MessageService;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import java.util.Date;
import java.util.List;
@Configuration
public class QuartzScheduler {
@Autowired
private Scheduler scheduler;
@Autowired
private MessageService messageService;
/**
* 开始执行所有任务
*/
public void startJob() throws SchedulerException {
//启动时进行查库将任务添加至job
List<MessageCron> netaskList = messageService.queryMessageCronList();
//添加任务至调度器scheduler
startJob1(scheduler,netaskList);
//调度任务开始执行
scheduler.start();
}
/*
* 重启所有任务
*/
public void restartJob() throws SchedulerException, InterruptedException {
//不可以用shutdown,也不需要停止,直接清除,然后启动
// scheduler.shutdown();
// scheduler.pauseAll();
scheduler.clear();
this.startJob();
}
/**
* title:
* mentality:
* @throws
* @param scheduler2
* @param zctaskList
*/
// private void startJob2(Scheduler scheduler2, List<NeTask> zctaskList) throws SchedulerException{
// TODO Auto-generated method stub
// }
/**
* title:计划任务1
* mentality:
* @throws
* @param scheduler2
* @param messageCronList
*/
private void startJob1(Scheduler scheduler2, List<MessageCron> messageCronList) throws SchedulerException{
// TODO Auto-generated method stub
// 通过JobBuilder构建JobDetail实例,JobDetail规定只能是实现Job接口的实例
// JobDetail 是具体Job实例
for(MessageCron messageCron : messageCronList){
JobDetail jobDetail = JobBuilder.newJob(NeTaskJob.class)//不同的业务,增加不同的.class
.withIdentity(messageCron.getId().toString())
.build();
jobDetail.getJobDataMap().put("id",messageCron);
// 基于表达式构建触发器
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(
//下面的cron你可以直接写个cron表达式来做验证,入:每隔5秒执行一次:*/5 * * * * ?
messageCron.getCron()
);
// CronTrigger表达式触发器 继承于Trigger
// TriggerBuilder 用于构建触发器实例
CronTrigger cronTrigger = TriggerBuilder.newTrigger()
//若一个jobdetail有多个trigger,则需要注意命名规则,便于后面修改任务
.forJob(jobDetail)
.withIdentity(messageCron.getId().toString())
.withSchedule(cronScheduleBuilder).build();
// scheduleJob该接口的作用是在将任务加入Quartz的同时绑定一个Trigger,Quartz会在加入该任务后自动设置Trigger的JobName与JobGroup为该JobDetail的name与group
scheduler2.scheduleJob(jobDetail, cronTrigger);//第一次必须有jobdetail
//rescheduleJob(String, String, Trigger) 替换一个指定的Trigger, 即解除指定Trigger与任务的绑定,并将新的Trigger与任务绑定,Quartz会自动调整新Trigger的JobName与JobGroup,而旧的Trigger将被移除
//Scheduler#triggerJob(String, String) 创建一个立即触发的Trigger,并将其与name与group指定的任务绑定
}
}
/**
* 获取Job信息
*
* @param name
* @return
* @throws SchedulerException
*/
public String getJobInfo(String name) throws SchedulerException {
TriggerKey triggerKey = new TriggerKey(name);
CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);
return String.format("time:%s,state:%s", cronTrigger.getCronExpression(),
scheduler.getTriggerState(triggerKey).name());
}
/**
* 新增一个定时任务
* @param name
* @param time
* @throws SchedulerException
*/
public void addJob(String name, String time)throws SchedulerException{
JobDetail jobDetail = JobBuilder.newJob(NeTaskJob.class)//不同的业务,增加不同的.class
.withIdentity(name)
.build();
MessageCron messageCron = new MessageCron();
messageCron.setId(Integer.valueOf(name));
messageCron.setCron(time);
jobDetail.getJobDataMap().put("id",messageCron);
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(
messageCron.getCron()
);
// CronTrigger表达式触发器 继承于Trigger
// TriggerBuilder 用于构建触发器实例
CronTrigger cronTrigger = TriggerBuilder.newTrigger()
.forJob(jobDetail)
.withIdentity(messageCron.getId().toString())
.withSchedule(cronScheduleBuilder).build();
scheduler.scheduleJob(jobDetail, cronTrigger);//第一次必须有jobdetail
}
/**
* 修改某个任务的执行时间
* (修改的是具体的trigger,不是jobdetail)
* @param name
* @param time
* @return
* @throws SchedulerException
*/
public boolean modifyJob(String name,String time) throws SchedulerException {
Date date = null;
TriggerKey triggerKey = new TriggerKey(name);
CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);
String oldTime = cronTrigger.getCronExpression();
if (!oldTime.equalsIgnoreCase(time)) {
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(time);
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(name)
.withSchedule(cronScheduleBuilder).build();
date = scheduler.rescheduleJob(triggerKey, trigger);
}
return date != null;
}
/**
* 暂停所有任务
*
* @throws SchedulerException
*/
public void pauseAllJob() throws SchedulerException {
scheduler.pauseAll();
}
/**
* 暂停某个任务
*
* @param name
* @throws SchedulerException
*/
public void pauseJob(String name) throws SchedulerException {
JobKey jobKey = new JobKey(name);
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
if (jobDetail == null){
return;
}
scheduler.pauseJob(jobKey);
}
/**
* 恢复所有任务
*
* @throws SchedulerException
*/
public void resumeAllJob() throws SchedulerException {
scheduler.resumeAll();
}
/**
* 恢复某个任务
*
* @param name
* @throws SchedulerException
*/
public void resumeJob(String name) throws SchedulerException {
JobKey jobKey = new JobKey(name);
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
if (jobDetail == null){
return;
}
scheduler.resumeJob(jobKey);
}
/**
* 删除某个任务
*
* @param name
* @throws SchedulerException
*/
public void deleteJob(String name) throws SchedulerException {
JobKey jobKey = new JobKey(name);
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
if (jobDetail == null){
return;
}
scheduler.deleteJob(jobKey);
}
}
NeTaskJob类:任务类,这里的关键是@PostConstruct,这是我们在实际项目中要调用其他接口的时候要用到的
package com.ksxx.platform.auth.schedule;
import com.ksxx.message.auth.po.MessageCron;
import com.ksxx.platform.auth.service.MessageService;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Component
public class NeTaskJob implements Job {
@Autowired
private MessageService messageService;
public static NeTaskJob neTaskJob;
public NeTaskJob(){}
//注入要调用的方法
@PostConstruct
public void init(){
neTaskJob = this;
neTaskJob.messageService = this.messageService;
}
@Override
public void execute(JobExecutionContext JobExecutionContext) throws JobExecutionException {
// TODO Auto-generated method stub
System.out.println("任务开始:"+System.currentTimeMillis());
// TODO 业务:此处是关键,如何从map中取得对应的job名称(netask的唯一标识id)
MessageCron messageCron = (MessageCron)JobExecutionContext.getMergedJobDataMap().get("id");
//实际项目中经常要调用到其他的接口方法,那么一定要在上方注入PostConstruct
neTaskJob.messageService.updateMessageStatus(messageCron.getId());
}
}
AppListener类:
package com.ksxx.platform.auth.schedule;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.ContextStartedEvent;
import org.springframework.stereotype.Component;
@Component
public class AppListener implements ApplicationListener<ApplicationEvent> {
private static boolean loaded = false;
@Autowired
private QuartzScheduler quartzScheduler;
@Override
public void onApplicationEvent(ApplicationEvent e){
if(e instanceof ContextRefreshedEvent){
if(!loaded){//避免多次执行
loaded = true;
//定时任务启动
try {
quartzScheduler.startJob();
System.out.println("任务已经启动...");
} catch (SchedulerException se) {
se.printStackTrace();
}
}
if(e instanceof ContextStartedEvent){
}
}
}
/**
* 初始注入scheduler
* @return
* @throws SchedulerException
*/
@Bean
public Scheduler scheduler() throws SchedulerException{
SchedulerFactory schedulerFactoryBean = new StdSchedulerFactory();
return schedulerFactoryBean.getScheduler();
}
}