定时任务:Quartz
Quartz简介
Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。
quartz是开源且具有丰富特性的"任务调度库",能够集成于任何的java应用,小到独立的应用,大至电子商业系统。quartz能够创建亦简单亦复杂的调度,以执行上十、上百,甚至上万的任务。任务job被定义为标准的java组件,能够执行任何你想要实现的功能。quartz调度框架包含许多企业级的特性,如JTA事务、集群的支持。
简而言之,quartz就是基于java实现的任务调度框架,用于执行你想要执行的任何任务。
官网:http://www.quartz-scheduler.org/
Quartz体系结构
jobdetail和trigger的关系是1对n
Quartz常用API
以下是Quartz编程API几个重要接口,也是Quartz的重要组件
- Scheduler:用于与调度程序交互的主程序接口。通过Scheduler来绑定jobdetail和trigger。只有安排进执行计划的任务Job(通过scheduler.scheduleJob方法安排进执行计划),当它预先定义的执行时间到了的时候(任务触发trigger),该任务才会执行。
- Job:预先定义的任务逻辑。
- JobDetail:定时任务的实例,JobDetail实例是通过JobBuilder类创建的,Scheduler真正调度的是JobDetail而不是job。
- JobDataMap:可以包含不限量的(序列化的)数据对象,可以看作JobDetail内业务逻辑运行时的需要的一系列参数。
- Trigger :触发器,Trigger对象是用来触发执行Job的。当调度一个job时,我们实例一个触发器然后调整它的属性来满足job执行的条件。表明任务在什么时候会执行。定义了一个已经被安排的任务将会在什么时候执行的时间条件,比如每2秒就执行一次。
- JobBuilder :用于声明一个任务实例,也可以定义关于该任务的详情比如任务名、组名等,这个声明的实例将会作为一个实际执行的任务。
- TriggerBuilder :用于声明触发器trigger实例。
- JobListener、TriggerListener、SchedulerListener:监听器,用于对组件的监听。
- ......
两个重要注解
@DisallowConcurrentExecution
作用:禁止同一任务定义的多个实例并发执行,确保任务在前一次执行完成后才会触发下一次执行。
解释:一个定时任务5秒钟触发一次,执行一次需要10分钟。打上这个注解后,在打上这个注解后,在上一个任务执行结束前,即使已经到了它的执行时间,也要等待上一个任务执行结束。
@PersistJobDataAfterExecution
作用:在执行完成后持久化JobDataMap中的数据,使任务状态可以在多次执行间保持。
解释:在任务中修改JobDataMap中的数据,此番修改是永久有效的。
案例
入门案例
新建一个springboot项目,导入quartz依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
写一个HelloJob任务类实现Job接口,重写execute方法
public class HelloJob implements Job {@Overridepublic void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {// 定义时间Date date = new Date();SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String dateString = dateFormat.format(date);System.out.println("进行数据库备份操作。当前任务执行的时间:" + dateString);}
}
声明JobDetail指定HelloJob,声明Trigger,绑定它俩交给Scheduler调度
public class HelloSchedulerDemo {public static void main(String[] args) throws Exception {// 1:从工厂中获取任务调度的实例Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();// 2:定义一个任务调度实例,将该实例与HelloJob绑定,任务类需要实现Job接口JobDetail job = JobBuilder.newJob(HelloJob.class).withIdentity("job1", "group1") // 定义该实例唯一标识.build();// 3:定义触发器 ,马上执行, 然后每5秒重复执行一次Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group1") // 定义该实例唯一标识.startNow() // 马上执行.withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatSecondlyForever(5)) // 简单间隔触发,每5秒执行一次.build();// 4:使用触发器调度任务的执行scheduler.scheduleJob(job, trigger);// 5:开启scheduler.start();// 关闭// scheduler.shutdown();}
}
使用JobDataMap
当Scheduler调用一个Job,就会将JobExecutionContext传递给Job的execute()方法。Job能通过JobExecutionContext对象访问到Quartz运行时候的环境以及Job本身的明细数据。当然,JobDataMap也在JobExecutionContext里面。
JobDataMap里面的值的获取方式
(1)使用Map获取。
- 在进行任务调度时,JobDataMap存储在JobExecutionContext中 ,非常方便获取。
- JobDataMap可以用来装载任何可序列化的数据对象,当job实例对象被执行时这些参数对象会传递给它。
- JobDataMap实现了JDK的Map接口,并且添加了非常方便的方法用来存取基本数据类型。
(2)Job实现类中添加setter方法对应JobDataMap的键值,Quartz框架默认的JobFactory实现类在初始化job实例对象时会自动地调用这些setter方法。
@PersistJobDataAfterExecution
public class HelloJob implements Job {private String name;@Overridepublic void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {System.out.println("Hello, " + name + "!");setName("I have changed my name");}public void setName(String name) {this.name = name;}public String getName() {return name;}
}
public class HelloJob implements Job {@Overridepublic void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {// 定义时间Date date = new Date();SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String dateString = dateFormat.format(date);// 定义工作任务内容System.out.println("进行数据库备份操作。当前任务执行的时间:" + dateString);System.out.println("name from job detail:"+jobExecutionContext.getJobDetail().getJobDataMap().get("name"));System.out.println("name from trigger:" + jobExecutionContext.getTrigger().getJobDataMap().get("name"));System.out.println("name from merge:" + jobExecutionContext.getMergedJobDataMap().get("name"));System.out.println("age:"+jobExecutionContext.getJobDetail().getJobDataMap().get("age"));}
}
public class HelloSchedulerDemo {public static void main(String[] args) throws Exception {// 1:从工厂中获取任务调度的实例Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();// 2:定义一个任务调度实例,将该实例与HelloJob绑定,任务类需要实现Job接口JobDetail job = JobBuilder.newJob(HelloJob.class).withIdentity("job1", "group1") // 定义该实例唯一标识.usingJobData("name","cjj").usingJobData("age",18).build();// 3:定义触发器 ,马上执行, 然后每5秒重复执行一次Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group1") // 定义该实例唯一标识.startNow() // 马上执行.withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatSecondlyForever(5)) // 简单间隔触发,每5秒执行一次.usingJobData("name","hlyy").build();// 4:使用触发器调度任务的执行scheduler.scheduleJob(job, trigger);// 5:开启scheduler.start();// 关闭// scheduler.shutdown();}
}
输出如下
进行数据库备份操作。当前任务执行的时间:2025-04-25 14:46:39
name from job detail:cjj
name from trigger:hlyy
name from merge:hlyy
age:18
这里注意:如果遇到同名的key,Trigger的优先级更高
有状态的Job
@PersistJobDataAfterExecution
public class HelloJob implements Job {private int count;@Overridepublic void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {// 定义时间Date date = new Date();SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String dateString = dateFormat.format(date);// 定义工作任务内容System.out.println("进行数据库备份操作。当前任务执行的时间:" + dateString + " 执行第 " + (++count) + " 次");// 保存到JobDataMap中jobExecutionContext.getJobDetail().getJobDataMap().put("count", count);}public void setCount(int count) {this.count = count;}
}
注意加上@PersistJobDataAfterExecution注解,才会每次保存count的状态,否则每次该JobDetail被调度时,都会将count初始化为0
public class HelloSchedulerDemo {public static void main(String[] args) throws Exception {// 1:从工厂中获取任务调度的实例Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();// 2:定义一个任务调度实例,将该实例与HelloJob绑定,任务类需要实现Job接口JobDetail job = JobBuilder.newJob(HelloJob.class).withIdentity("job1", "group1") // 定义该实例唯一标识.usingJobData("count",0).build();// 3:定义触发器 ,马上执行, 然后每5秒重复执行一次Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group1") // 定义该实例唯一标识.startNow() // 马上执行.withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatSecondlyForever(5)) // 简单间隔触发,每5秒执行一次.build();// 4:使用触发器调度任务的执行scheduler.scheduleJob(job, trigger);// 5:开启scheduler.start();// 关闭// scheduler.shutdown();}
}
4:54:32.523 [DefaultQuartzScheduler_Worker-1] DEBUG org.quartz.core.JobRunShell - Calling execute on job group1.job1
进行数据库备份操作。当前任务执行的时间:2025-04-25 14:54:32 执行第 1 次
14:54:37.524 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.simpl.PropertySettingJobFactory - Producing instance of Job 'group1.job1', class=org.daolong.job.HelloJob
14:54:37.525 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.core.QuartzSchedulerThread - batch acquisition of 1 triggers
14:54:37.525 [DefaultQuartzScheduler_Worker-2] DEBUG org.quartz.core.JobRunShell - Calling execute on job group1.job1
进行数据库备份操作。当前任务执行的时间:2025-04-25 14:54:37 执行第 2 次
14:54:42.517 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.simpl.PropertySettingJobFactory - Producing instance of Job 'group1.job1', class=org.daolong.job.HelloJob
14:54:42.518 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.core.QuartzSchedulerThread - batch acquisition of 1 triggers
14:54:42.518 [DefaultQuartzScheduler_Worker-3] DEBUG org.quartz.core.JobRunShell - Calling execute on job group1.job1
进行数据库备份操作。当前任务执行的时间:2025-04-25 14:54:42 执行第 3 次
Cron触发器
public class HelloSchedulerDemoCronTrigger {public static void main(String[] args) throws Exception {// 1:从工厂中获取任务调度的实例Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();// 2:定义一个任务调度实例,将该实例与HelloJob绑定,任务类需要实现Job接口JobDetail job = JobBuilder.newJob(HelloJob.class).withIdentity("job1", "group1") // 定义该实例唯一标识.build();// 3:定义触发器 ,马上执行, 然后每5秒重复执行一次Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group1") // 定义该实例唯一标识.withSchedule(CronScheduleBuilder.cronSchedule("*/5 * * * * ?")) // 每5秒执行一次.build();// 4:使用触发器调度任务的执行scheduler.scheduleJob(job, trigger);// 5:开启scheduler.start();// 关闭// scheduler.shutdown();}
}
监听器(以SchedulerListener为例)
自定义一个MySchedulerListener实现SchedulerListener接口
public class MySchedulerListener implements SchedulerListener {@Overridepublic void jobScheduled(Trigger trigger) {String jobName = trigger.getJobKey().getName();System.out.println(jobName + " 完成部署");}@Overridepublic void jobUnscheduled(TriggerKey triggerKey) {System.out.println(triggerKey + " 完成卸载");}@Overridepublic void triggerFinalized(Trigger trigger) {System.out.println("触发器被移除 " + trigger.getJobKey().getName());}@Overridepublic void triggerPaused(TriggerKey triggerKey) {System.out.println(triggerKey + " 正在被暂停");}@Overridepublic void triggersPaused(String triggerGroup) {System.out.println("触发器组 "+triggerGroup + " 正在被暂停");}@Overridepublic void triggerResumed(TriggerKey triggerKey) {System.out.println(triggerKey + " 正在从暂停中恢复");}@Overridepublic void triggersResumed(String triggerGroup) {System.out.println("触发器组 "+triggerGroup + " 正在从暂停中恢复");}@Overridepublic void jobAdded(JobDetail jobDetail) {System.out.println(jobDetail.getKey()+" 添加工作任务");}@Overridepublic void jobDeleted(JobKey jobKey) {System.out.println(jobKey+" 删除工作任务");}@Overridepublic void jobPaused(JobKey jobKey) {System.out.println(jobKey+" 工作任务正在被暂停");}@Overridepublic void jobsPaused(String jobGroup) {System.out.println("工作任务组 "+jobGroup+" 正在被暂停");}@Overridepublic void jobResumed(JobKey jobKey) {System.out.println(jobKey+" 正在从暂停中恢复");}@Overridepublic void jobsResumed(String jobGroup) {System.out.println("工作任务组 "+jobGroup+" 正在从暂停中恢复");}@Overridepublic void schedulerError(String msg, SchedulerException cause) {System.out.println("产生严重错误时调用: "+msg+" "+cause.getUnderlyingException());}@Overridepublic void schedulerInStandbyMode() {System.out.println("调度器在挂起模式下调用");}@Overridepublic void schedulerStarted() {System.out.println("调度器 开启时调用");}@Overridepublic void schedulerStarting() {System.out.println("调度器 正在开启时调用");}@Overridepublic void schedulerShutdown() {System.out.println("调度器 已经被关闭 时调用");}@Overridepublic void schedulerShuttingdown() {System.out.println("调度器 正在被关闭 时调用");}@Overridepublic void schedulingDataCleared() {System.out.println("调度器的数据被清除时调用");}
}
public class HelloSchedulerDemoSchedulerListener {public static void main(String[] args) throws Exception {// 1:从工厂中获取任务调度的实例Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();// 2:定义一个任务调度实例,将该实例与HelloJob绑定,任务类需要实现Job接口JobDetail job = JobBuilder.newJob(HelloJob.class).withIdentity("job1", "group1") // 定义该实例唯一标识.build();// 3:定义触发器 ,马上执行, 然后每5秒重复执行一次Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group1") // 定义该实例唯一标识.startNow() // 马上执行.withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatSecondlyForever(5)) // 每5秒执行一次.build();// 4:使用触发器调度任务的执行scheduler.scheduleJob(job, trigger);// 创建SchedulerListenerscheduler.getListenerManager().addSchedulerListener(new MySchedulerListener());// 移除对应的SchedulerListener// scheduler.getListenerManager().removeSchedulerListener(new MySchedulerListener());// 5:开启scheduler.start();// 延迟7秒后关闭Thread.sleep(7000);// 关闭scheduler.shutdown();}
}
SpringBoot整合Quartz,实现一个Quartz任务调度中心
参考文章:Spring Boot整合Quartz实现动态配置
完整代码
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-quartz</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><!-- 兼容 Spring Boot 2.6.x 的版本 --><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.9.2</version></dependency><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger-ui</artifactId><version>2.9.2</version></dependency><!-- 美化ui--><dependency><groupId>com.github.xiaoymin</groupId><artifactId>swagger-bootstrap-ui</artifactId><version>1.9.6</version></dependency><!-- 校验框架--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency></dependencies>
# MySQL 数据源配置
spring.datasource.url=jdbc:mysql://localhost:3306/quartz_db?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver# Quartz 持久化配置
spring.quartz.job-store-type=jdbc
# 启动时不初始化数据库,我们自己手动初始化过了
spring.quartz.jdbc.initialize-schema=embedded# Quartz 集群配置
#spring.quartz.properties.org.quartz.jobStore.isClustered: true
#spring.quartz.properties.org.quartz.jobStore.tablePrefix: QRTZ_
#spring.quartz.properties.org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
在maven的依赖包下找到Quartz 调度器所需的表结构的 SQL 脚本文件,到数据库中手动执行。
E:\apache-maven-3.8.6\repository\org\quartz-scheduler\quartz\2.3.2\quartz-2.3.2.jar!\org\quartz\impl\jdbcjobstore\tables_mysql_innodb.sql
@Configuration
public class QuartzConfig {@Beanpublic Scheduler myScheduler(SchedulerFactoryBean schedulerFactoryBean) throws Exception {Scheduler scheduler = schedulerFactoryBean.getScheduler();scheduler.start();scheduler.getListenerManager().addJobListener(new MyJobListener());return scheduler;}
}
@Component
public class QuartzTestJob extends QuartzJobBean {@Overrideprotected void executeInternal(org.quartz.JobExecutionContext jobExecutionContext) throws JobExecutionException {System.out.println("Quartz Test Job");}
}
public interface QuartzService {/*** 添加定时任务*/void addJob(QuartzCreateParam param) throws SchedulerException;/*** 修改定时任务*/void updateJob(QuartzUpdateParam param) throws SchedulerException;/*** 暂停定时任务*/void pauseJob(QuartzDetailParam param) throws SchedulerException;/*** 恢复定时任务*/void resumeJob(QuartzDetailParam param) throws SchedulerException;/*** 删除定时任务*/void deleteJob(QuartzDetailParam param) throws SchedulerException;/*** 定时任务列表* @return*/List<QuartzJobDetailDto> jobList() throws SchedulerException;/*** 定时任务详情*/QuartzJobDetailDto jobDetail(QuartzDetailParam param) throws SchedulerException;
}
@Service
public class QuartzServiceImpl implements QuartzService {@Resource(name = "myScheduler")private Scheduler scheduler;@Overridepublic void addJob(QuartzCreateParam param) throws SchedulerException {String clazzName = param.getJobClazz();String jobName = param.getJobName();String jobGroup = param.getJobGroup();String triggerName = param.getTriggerName();String triggerGroup = param.getTriggerGroup();String cron = param.getCron();String description = param.getDescription();JobKey jobKey = JobKey.jobKey(jobName, jobGroup);if (checkExist(jobKey)) {throw new BaseException("要添加的任务已经存在:" + jobKey);}Class<? extends Job> jobClass = null;try {jobClass = (Class<? extends Job>) Class.forName(clazzName);} catch (ClassNotFoundException e) {throw new BaseException("找不到任务类:" + clazzName);}JobDataMap jobDataMap = new JobDataMap();if (param.getJobDataMap() != null) {jobDataMap.putAll(param.getJobDataMap());}JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroup).usingJobData(jobDataMap).build();CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);Trigger trigger = TriggerBuilder.newTrigger().withSchedule(scheduleBuilder).withIdentity(TriggerKey.triggerKey(triggerGroup, triggerName)).withDescription(description).build();scheduler.scheduleJob(jobDetail, trigger);if (!scheduler.isShutdown()) {scheduler.start();}}/*** 可能出现的情况* 1.修改该任务现指定的触发器的cron表达式 (给出triggerName、triggerGroup、cron)* 2.为该任务绑定一个新的(新增)触发器 (给出triggerName、triggerGroup、cron)* 3.为该任务绑定一个已有触发器 (给出triggerName、triggerGroup)*/@Overridepublic void updateJob(QuartzUpdateParam param) throws SchedulerException {String jobName = param.getJobName();String jobGroup = param.getJobGroup();String triggerName = param.getTriggerName();String triggerGroup = param.getTriggerGroup();String cron = param.getCron();JobKey jobKey = JobKey.jobKey(jobName, jobGroup);TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroup);if (!checkExist(jobKey)) {throw new BaseException("要修改的任务不存在:" + jobKey);}// 情况3if (cron.isEmpty()) {if (!checkExist(triggerKey)) {throw new BaseException("要绑定的任务触发器不存在:" + triggerKey);}scheduler.scheduleJob(scheduler.getTrigger(triggerKey));return;}// 情况1if (checkExist(triggerKey)) {// 获取原触发器的状态Trigger.TriggerState triggerState = scheduler.getTriggerState(triggerKey);CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);Trigger trigger = TriggerBuilder.newTrigger().withSchedule(scheduleBuilder).withIdentity(triggerKey).build();/** 更新触发器* 停止旧触发器的调度计划。* 将新触发器绑定到原任务(JobDetail)。* 根据新触发器的配置(如 Cron 表达式、开始/结束时间)生成下一次触发时间。* 根据原触发器的状态选择是否继续调度。*/scheduler.rescheduleJob(triggerKey, trigger);if (triggerState == Trigger.TriggerState.PAUSED) {scheduler.pauseTrigger(triggerKey);}return;}// 情况2CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);Trigger trigger = TriggerBuilder.newTrigger().withSchedule(scheduleBuilder).withIdentity(triggerKey).build();scheduler.scheduleJob(trigger);}@Overridepublic void pauseJob(QuartzDetailParam param) throws SchedulerException {String jobName = param.getJobName();String jobGroup = param.getJobGroup();JobKey jobKey = JobKey.jobKey(jobName, jobGroup);if (checkExist(jobKey)) {scheduler.pauseJob(jobKey);} else {throw new BaseException("要暂停的任务不存在:" + jobKey);}}@Overridepublic void resumeJob(QuartzDetailParam param) throws SchedulerException {String jobName = param.getJobName();String jobGroup = param.getJobGroup();JobKey jobKey = JobKey.jobKey(jobName, jobGroup);if (checkExist(jobKey)) {if (!scheduler.isShutdown()) {scheduler.resumeJob(jobKey);return;}}throw new BaseException("要恢复的任务不存在:" + jobKey);}@Overridepublic void deleteJob(QuartzDetailParam param) throws SchedulerException {String jobName = param.getJobName();String jobGroup = param.getJobGroup();JobKey jobKey = JobKey.jobKey(jobName, jobGroup);if (!checkExist(jobKey)) {throw new BaseException("要删除的任务不存在:" + jobKey);}// 先暂停再删除scheduler.pauseJob(jobKey);scheduler.deleteJob(jobKey);}@Overridepublic List<QuartzJobDetailDto> jobList() throws SchedulerException {// 获取匹配所有任务的matcherGroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();List<QuartzJobDetailDto> jobDtoList = new ArrayList<>();Set<JobKey> jobKeySet = scheduler.getJobKeys(matcher);for (JobKey jobKey : jobKeySet) {QuartzJobDetailDto jobDto = getJobDtoByJobKey(jobKey);jobDtoList.add(jobDto);}return jobDtoList;}@Overridepublic QuartzJobDetailDto jobDetail(QuartzDetailParam param) throws SchedulerException {String jobName = param.getJobName();String jobGroup = param.getJobGroup();JobKey jobKey = JobKey.jobKey(jobName, jobGroup);return getJobDtoByJobKey(jobKey);}// 检查任务是否存在, 如果存在返回trueprivate Boolean checkExist(JobKey jobKey) throws SchedulerException {return scheduler.checkExists(jobKey);}// 检查触发器是否存在, 如果存在返回trueprivate Boolean checkExist(TriggerKey triggerKey) throws SchedulerException {return scheduler.checkExists(triggerKey);}// 根据jobKey获取任务详情public QuartzJobDetailDto getJobDtoByJobKey(JobKey jobKey) throws SchedulerException {JobDetail jobDetail = scheduler.getJobDetail(jobKey);List<Trigger> triggerList = (List<Trigger>) scheduler.getTriggersOfJob(jobKey);QuartzJobDetailDto jobDto = new QuartzJobDetailDto();jobDto.setJobClazz(jobDetail.getJobClass().toString());jobDto.setJobName(jobKey.getName());jobDto.setJobGroup(jobKey.getGroup());jobDto.setJobDataMap(jobDetail.getJobDataMap());List<QuartzTriggerDetailDto> triggerDtoList = new ArrayList<>();for (Trigger trigger : triggerList) {QuartzTriggerDetailDto triggerDto = new QuartzTriggerDetailDto();triggerDto.setTriggerName(trigger.getKey().getName());triggerDto.setTriggerGroup(trigger.getKey().getGroup());triggerDto.setDescription(trigger.getDescription());if (trigger instanceof CronTriggerImpl) {CronTriggerImpl cronTriggerImpl = (CronTriggerImpl) trigger;String cronExpression = cronTriggerImpl.getCronExpression();triggerDto.setCron(cronExpression);// 计算最近10次的触发时间List<Date> dates = TriggerUtils.computeFireTimes(cronTriggerImpl, null, 10);triggerDto.setRecentFireTimeList(dates);}Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());triggerDto.setTriggerState(triggerState.toString());triggerDtoList.add(triggerDto);}jobDto.setTriggerDetailDtoList(triggerDtoList);return jobDto;}}