生命无罪,健康万岁,我是laity。
我曾七次鄙视自己的灵魂:
第一次,当它本可进取时,却故作谦卑;
第二次,当它在空虚时,用爱欲来填充;
第三次,在困难和容易之间,它选择了容易;
第四次,它犯了错,却借由别人也会犯错来宽慰自己;
第五次,它自由软弱,却把它认为是生命的坚韧;
第六次,当它鄙夷一张丑恶的嘴脸时,却不知那正是自己面具中的一副;
第七次,它侧身于生活的污泥中,虽不甘心,却又畏首畏尾。
本文带各位学习下Quartz的基本使用及业务中的整合,包括基本概念以及如何动态地对定时任务进行CRUD,并且如何实现定时任务的持久化以及任务恢复;其中分享下本人在使用时遇到的问题,和解决方案。
Quartz的基本使用
Quartz 是一个开源的作业调度框架,支持分布式定时任务,Quartz定时任务据我了解可分为Trigger(触发器)、Job(任务)和Scheduler(调度器),定时任务的逻辑大体为:创建触发器和任务,并将其加入到调度器中。
Quartz 的核心类有以下三部分:
任务 Job : 需要实现的任务类,实现 execute() 方法,执行后完成任务;
触发器 Trigger : 包括 SimpleTrigger 和 CronTrigger;
调度器 Scheduler : 任务调度器,负责基于 Trigger触发器,来执行 Job任务.
Trigger 有五种触发器:
SimpleTrigger 触发器:需要在特定的日期/时间启动,且以指定的间隔时间(单位毫秒)重复执行 n 次任务,如 :在 9:00 开始,每隔1小时,每隔几分钟,每隔几秒钟执行一次 。没办法指定每隔一个月执行一次(每月的时间间隔不是固定值)。
CalendarIntervalTrigger 触发器:指定从某一个时间开始,以一定的时间间隔(单位有秒,分钟,小时,天,月,年,星期)执行的任务。
DailyTimeIntervalTrigger 触发器:指定每天的某个时间段内,以一定的时间间隔执行任务。并且支持指定星期。如:指定每天 9:00 至 18:00 ,每隔 70 秒执行一次,并且只要周一至周五执行。
CronTrigger 触发器:基于日历的任务调度器,即指定星期、日期的某时间执行任务。
NthIncludedDayTrigger 触发器:不同时间间隔的第 n 天执行任务。比如,在每个月的第 15 日处理财务发票记帐,同样设定双休日或者假期。 使用场景
发布消息、问卷等信息时,发布者可以指定星期、月份的具体时间进行定时发布(cron 触发器)设置当天或指定日期的时间范围内,指定时间间隔执行任务。其他定时功能可根据不同的任务触发器进行实现。依赖的引入
org.springframework.bootspring-boot-starter-quartz简单的测试
将job封装给jobDetail,由调度器scheudler根据触发器trggier条件触发相应的jobDetail,每次触发都会让jobDetail重新创建job对象,并且jobDetail会将数据传给job
有两种方式:
1.jobDetail会根据自己usingJobData中的参数主动调用job对应的set方法,设置给job使用。
2.*job可以从重写方法传过来的参数jobExecutionContext中获取jobDetail,*然后从jobDetail中获取到jobDataMap。
/** * @author: Laity * @Project: JavaLaity * @Description: 测试定时任务并获取自定义参数 */ public class MyJob implements Job { @Override public void execute(JobExecutionContext content) throws JobExecutionException { long count = (long) content.getJobDetail().getJobDataMap().get("count"); System.out.println("当前执行,第"+ count +"次"); content.getJobDetail().getJobDataMap().put("count", ++count); System.out.println("任务执行....."); } public static void main(String[] args) throws Exception { // 1.创建调度器 Scheduler SchedulerFactory factory = new StdSchedulerFactory(); Scheduler scheduler = factory.getScheduler(); // 2.创建JobDetail实例,并与MyJob类绑定(Job执行内容) JobDetail job = JobBuilder.newJob(MyJob.class) .withIdentity("job1","group1") .usingJobData("count", 1L) .build(); // 3.构建Trigger实例,每隔3s执行一次 Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1","group1") .startNow() .withSchedule(simpleSchedule() .withIntervalInSeconds(3) .repeatForever()) .build(); // 4.执行,开启调度器 scheduler.scheduleJob(job, trigger); System.out.println(System.currentTimeMillis()); scheduler.start(); //主线程睡眠1分钟,然后关闭调度器 TimeUnit.MINUTES.sleep(1); scheduler.shutdown(); System.out.println(System.currentTimeMillis()); } }
Quartz高级使用
当遇到更新版本等情况时,肯定要将程序给停了,但是程序停止后那些还未开始或者没执行完的定时任务就没了。所以我们需要将任务持久化到数据库中,然后在程序启动时将这些任务进行恢复。
数据库表设计 官方提供了一份数据库表设计,有兴趣的小伙伴可以去下载 DROP TABLE IF EXISTS `quartz_entity`; CREATE TABLE `quartz_entity` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', `job_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '任务名', `group_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '任务分组', `start_time` timestamp DEFAULT NULL COMMENT '任务开始时间', `end_time` timestamp DEFAULT NULL COMMENT '任务结束时间', `job_class` varchar(255) DEFAULT NULL COMMENT '定时任务所在的类', `cron` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT 'cron表达式', `job_data_map_json` varchar(255) DEFAULT NULL COMMENT 'json格式的jobDataMap', `status` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '0' COMMENT '任务状态。0-进行中;1-已完成;2-取消', PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COMMENT='定时任务信息'; SET FOREIGN_KEY_CHECKS = 1; application-local.yml配置 spring: info: build: encoding: UTF-8 datasource: dynamic: druid: initial-size: 10 # 初始化大小,最小,最大 min-idle: 20 maxActive: 500 # 配置获取连接等待超时的时间 maxWait: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 timeBetweenEvictionRunsMillis: 60000 # 配置一个连接在池中最小生存的时间,单位是毫秒 minEvictableIdleTimeMillis: 300000 testWhileIdle: true testOnBorrow: true validation-query: SELECT 1 testOnReturn: false # 打开PSCache,并且指定每个连接上PSCache的大小 poolPreparedStatements: true maxPoolPreparedStatementPerConnectionSize: 20 filters: stat,wall filter: wall: config: multi-statement-allow: true none-base-statement-allow: true enabled: true # 配置DruidStatFilter web-stat-filter: enabled: true url-pattern:"/*"exclusions:"*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"# 配置DruidStatViewServlet stat-view-servlet: enabled: true url-pattern:"/druid/*"allow: deny: reset-enable: false login-username: admin login-password: 111111 query-timeout: 36000 primary: slave strict: false datasource: master: url: jdbc:mysql://127.0.0.1:3306/jxgl?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true username: root password: wang9264 driver-class-name: com.mysql.jdbc.Driver slave: url: jdbc:mysql://127.0.0.1:3306/java?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true username: root password: wang9264 driver-class-name: com.mysql.jdbc.Driver 实体类的创建 /** * @author: Laity * @Project: JavaLaity */ @Data public class QuartzEntity { @TableId(value ="id",type = IdType.AUTO) private Long id; private String jobName; private String groupName; private Date startTime; private Date endTime; private String jobClass; private String cron; private String jobDataMapJson; private String status; } service层 /** * @author: Laity * @Project: JavaLaity */ public interface QuartzService { void save(QuartzEntity entity); boolean modifyJob(QuartzEntity entity); boolean modifyTaskStatus(String jobName,String status); List
notStartOrNotEndJobs(); } serviceImpl层 /** * @author: Laity * @Project: JavaLaity */ @Service("quartzService") public class QuartzServiceImpl implements QuartzService { @Resource private QuartzDao quartzMapper; @Override public void save(QuartzEntity entity) { quartzMapper.insert(entity); } @Override public boolean modifyJob(QuartzEntity entity) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(QuartzEntity::getJobName, entity.getJobName()); QuartzEntity one = quartzMapper.selectOne(wrapper); if (one != null) { entity.setId(one.getId()); return quartzMapper.updateById(entity) > 0; } return false; } @Override public boolean modifyTaskStatus(String jobName, String status) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(QuartzEntity::getJobName, jobName); QuartzEntity one = quartzMapper.selectOne(wrapper); if (one != null) { one.setStatus(status); return quartzMapper.updateById(one) > 0; } return false; } @Override public List notStartOrNotEndJobs() { return quartzMapper.notStartOrNotEndJobs(); } } Dao层 /** * @author: Laity * @Project: JavaLaity */ @Mapper public interface QuartzDao extends BaseMapper { @Select("SELECT"+"*"+"FROM"+"quartz_entity"+"WHERE"+"( end_time IS NULL"+ // 没有结束时间的"OR ( start_time < NOW() AND end_time > NOW())"+ // 已经开始但未结束的"OR start_time > NOW()"+ // 还未开始的")"+"AND `status` = '0'") List notStartOrNotEndJobs(); } 封装组件 QuartzUtil.java 封装了 定时任务的创建、定时任务的修改、定时任务的结束、定时任务的查询、定时任务的恢复(重启服务的时候使用)
/** * @author: Laity * @Project: JavaLaity */ @Component public class QuartzUtil { private static final SchedulerFactory SCHEDULER_FACTORY = new StdSchedulerFactory(); @Autowired private QuartzService quartzService; /** * 添加一个定时任务 * * @param name 任务名。每个任务唯一,不能重复。方便起见,触发器名也设为这个 * @param group 任务分组。方便起见,触发器分组也设为这个 * @param jobClass 任务的类类型 eg:MyJob.class * @param startTime 任务开始时间。传null就是立即开始 * @param endTime 任务结束时间。如果是一次性任务或永久执行的任务就传null * @param cron 时间设置表达式。传null就是一次性任务 */ public boolean addJob(String name, String group, Class extends Job> jobClass, Date startTime, Date endTime, String cron, JobDataMap jobDataMap) { try { // 第一步: 定义一个JobDetail JobDetail jobDetail = JobBuilder.newJob(jobClass). withIdentity(name, group).setJobData(jobDataMap).build(); // 第二步: 设置触发器 TriggerBuilder triggerBuilder = newTrigger(); triggerBuilder.withIdentity(name, group); triggerBuilder.startAt(toStartDate(startTime)); triggerBuilder.endAt(toEndDate(endTime)); //设为null则表示不会停止 if (StrUtil.isNotEmpty(cron)) { triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron)); } Trigger trigger = triggerBuilder.build(); //第三步:调度器设置 Scheduler scheduler = SCHEDULER_FACTORY.getScheduler(); scheduler.scheduleJob(jobDetail, trigger); if (!scheduler.isShutdown()) { scheduler.start(); } } catch (Exception e) { e.printStackTrace(); return false; } //存储到数据库中 QuartzEntity entity = new QuartzEntity(); entity.setJobName(name); entity.setGroupName(group); entity.setStartTime(startTime != null ? startTime : new Date()); entity.setEndTime(endTime); entity.setJobClass(jobClass.getName()); entity.setCron(cron); entity.setJobDataMapJson(JSONUtil.toJsonStr(jobDataMap)); entity.setStatus("0"); quartzService.save(entity); return true; } /** * 修改一个任务的开始时间、结束时间、cron。不改的就传null * * @param name 任务名。每个任务唯一,不能重复。方便起见,触发器名也设为这个 * @param group 任务分组。方便起见,触发器分组也设为这个 * @param newStartTime 新的开始时间 * @param newEndTime 新的结束时间 * @param cron 新的时间表达式 */ public boolean modifyJobTime(String name, String group, Date newStartTime, Date newEndTime, String cron) { try { Scheduler scheduler = SCHEDULER_FACTORY.getScheduler(); TriggerKey triggerKey = TriggerKey.triggerKey(name, group); Trigger oldTrigger = scheduler.getTrigger(triggerKey); if (oldTrigger == null) { return false; } TriggerBuilder triggerBuilder = newTrigger(); triggerBuilder.withIdentity(name, group); if (newStartTime != null) { triggerBuilder.startAt(toStartDate(newStartTime)); // 任务开始时间设定 } else if (oldTrigger.getStartTime() != null) { triggerBuilder.startAt(oldTrigger.getStartTime()); //没有传入新的开始时间就不变 } if (newEndTime != null) { triggerBuilder.endAt(toEndDate(newEndTime)); // 任务结束时间设定 } else if (oldTrigger.getEndTime() != null) { triggerBuilder.endAt(oldTrigger.getEndTime()); //没有传入新的结束时间就不变 } if (StrUtil.isNotEmpty(cron)) { triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron)); } else if (oldTrigger instanceof CronTrigger) { String oldCron = ((CronTrigger) oldTrigger).getCronExpression(); triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(oldCron)); } Trigger newTrigger = triggerBuilder.build(); scheduler.rescheduleJob(triggerKey, newTrigger); // 修改触发时间 } catch (Exception e) { e.printStackTrace(); return false; } // 修改数据库中的记录 QuartzEntity entity = new QuartzEntity(); entity.setJobName(name); entity.setGroupName(group); if (newStartTime != null) { entity.setStartTime(newStartTime); } if (newEndTime != null) { entity.setEndTime(newEndTime); } if (StrUtil.isNotEmpty(cron)) { entity.setCron(cron); } return quartzService.modifyJob(entity); } /** * 结束任务 * @param jobName 任务名称 * @param groupName 分组名称 * @return boolean */ public boolean cancelJob(String jobName, String groupName) { try { Scheduler scheduler = SCHEDULER_FACTORY.getScheduler(); TriggerKey triggerKey = TriggerKey.triggerKey(jobName, groupName); scheduler.pauseTrigger(triggerKey); // 停止触发器 scheduler.unscheduleJob(triggerKey); // 移除触发器 scheduler.deleteJob(JobKey.jobKey(jobName, groupName)); // 删除任务 } catch (Exception e) { e.printStackTrace(); return false; } //将数据库中的任务状态设为 取消 return quartzService.modifyTaskStatus(jobName,"2"); } /** * 获取所有job任务信息 * @return list * @throws SchedulerException error */ public List getAllJobs() throws SchedulerException { Scheduler scheduler = SCHEDULER_FACTORY.getScheduler(); List quartzJobs = new ArrayList<>(); try { List triggerGroupNames = scheduler.getTriggerGroupNames(); for (String groupName : triggerGroupNames) { GroupMatcher groupMatcher = GroupMatcher.groupEquals(groupName); Set triggerKeySet = scheduler.getTriggerKeys(groupMatcher); for (TriggerKey triggerKey : triggerKeySet) { Trigger trigger = scheduler.getTrigger(triggerKey); JobKey jobKey = trigger.getJobKey(); JobDetail jobDetail = scheduler.getJobDetail(jobKey); //组装数据 QuartzEntity entity = new QuartzEntity(); entity.setJobName(jobDetail.getKey().getName()); entity.setGroupName(jobDetail.getKey().getGroup()); entity.setStartTime(trigger.getStartTime()); entity.setEndTime(trigger.getStartTime()); entity.setJobClass(jobDetail.getJobClass().getName()); if (trigger instanceof CronTrigger) { entity.setCron(((CronTrigger) trigger).getCronExpression()); } entity.setJobDataMapJson(JSONUtil.toJsonStr(jobDetail.getJobDataMap())); quartzJobs.add(entity); } } } catch (Exception e) { e.printStackTrace(); } return quartzJobs; } public void recoveryAllJob() { List tasks = quartzService.notStartOrNotEndJobs(); if (tasks != null && tasks.size() > 0) { for (QuartzEntity task : tasks) { try { JobDataMap jobDataMap = JSONUtil.toBean(task.getJobDataMapJson(), JobDataMap.class); JobDetail jobDetail = JobBuilder.newJob((Class extends Job>) Class.forName(task.getJobClass())) .withIdentity(task.getJobName(), task.getGroupName()) .setJobData(jobDataMap).build(); TriggerBuilder triggerBuilder = TriggerBuilder.newTrigger(); triggerBuilder.withIdentity(task.getJobName(), task.getGroupName()); triggerBuilder.startAt(toStartDate(task.getStartTime())); triggerBuilder.endAt(toEndDate(task.getEndTime())); if (StrUtil.isNotEmpty(task.getCron())) { triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(task.getCron())); } Trigger trigger = triggerBuilder.build(); Scheduler scheduler = SCHEDULER_FACTORY.getScheduler(); scheduler.scheduleJob(jobDetail, trigger); if (!scheduler.isShutdown()) { scheduler.start(); } } catch (Exception e) { e.printStackTrace(); } } } } private static Date toEndDate(Date endDateTime) { // endDateTime为null时转换会报空指针异常,所以需要进行null判断。 // 结束时间可以为null,所以endDateTime为null,直接返回null即可 return endDateTime != null ? DateUtil.date(endDateTime) : null; } private static Date toStartDate(Date startDateTime) { // startDateTime为空时返回当前时间,表示立即开始 return startDateTime != null ? DateUtil.date(startDateTime) : new Date(); } } SpringContextJobUtil.java 用于获取Bean
/** * @author: Laity * @Project: JavaLaity */ @Component public class SpringContextJobUtil implements ApplicationContextAware { private static ApplicationContext context; @Override @SuppressWarnings("static-access") public void setApplicationContext(ApplicationContext context) throws BeansException { this.context = context; } public static Object getBean(String beanName) { return context.getBean(beanName); } } CronUtil.java 你不可能让用户来输入cron表达式,所以根据用户的选择来解析成cron表达式
package com.ys.control_core.util.job; import org.apache.commons.lang3.StringUtils; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * @author: Laity * @Project: JavaLaity * @Description: 用于生成Cron表达式 */ public class CronUtil { /** * 每天 */ private static final int DAY_JOB_TYPE = 1; /** * 每周 */ private static final int WEEK_JOB_TYPE = 2; /** * 每月 */ private static final int MONTH_JOB_TYPE = 3; /** * 构建Cron表达式 * * @param jobType 作业类型: 1/每天; 2/每周; 3/每月 * @param minute 指定分钟 * @param hour 指定小时 * @param lastDayOfMonth 指定一个月的最后一天:0/不指定;1/指定 * @param weekDays 指定一周哪几天:1/星期天; 2/...3/.. ; 7/星期六 * @param monthDays 指定一个月的哪几天 * @return String */ public static String createCronExpression(Integer jobType, Integer minute, Integer hour, Integer lastDayOfMonth, List weekDays, List monthDays) { StringBuilder cronExp = new StringBuilder(); // 秒 cronExp.append("0"); // 指定分钟,为空则默认0分 cronExp.append(minute == null ?"0": minute).append(""); // 指定小时,为空则默认0时 cronExp.append(hour == null ?"0": hour).append(""); // 每天 if (jobType == DAY_JOB_TYPE) { // 日 cronExp.append("*"); // 月 cronExp.append("*"); // 周 cronExp.append("?"); } else if (lastDayOfMonth != null && lastDayOfMonth == 1) { // 日 cronExp.append("L"); // 月 cronExp.append("*"); // 周 cronExp.append("?"); } // 按每周 else if (weekDays != null && jobType == WEEK_JOB_TYPE) { // 日 cronExp.append("?"); // 月 cronExp.append("*"); // 一个周的哪几天 cronExp.append(StringUtils.join(weekDays,",")); } // 按每月 else if (monthDays != null && jobType == MONTH_JOB_TYPE) { // 日 cronExp.append(StringUtils.join(monthDays,",")).append(""); // 月 cronExp.append("*"); // 周 cronExp.append("?"); } else { cronExp.append("*").append("*").append("?"); } return cronExp.toString(); } public static void main(String[] args) { String cronExpression = createCronExpression(1, 26, null, null, null, null); createCronExpression(2, 26, 9, 0, null, null); // 0/2 * * * * ? System.out.println(cronExpression); } } ValidCron.java 用于检验Cron表达式的正确性
/** * @author: Laity * @Project: JavaLaity */ public class ValidCronUtil { public static boolean isValidCronExpression(String exp) { if (exp == null || exp.length() ==0) return false; boolean validExpression = CronExpression.isValidExpression(exp); if (validExpression) System.out.println("cron expression is valid."); return validExpression; } public static void main(String[] args) { String cron ="0 26 9 ? * 1,2,3,4,5"; boolean validCronExpression = isValidCronExpression(cron); System.out.println(validCronExpression); } } Controller层 /** * @author: Laity * @Project: JavaLaity */ @RestController @RequestMapping("/quartz/web") @Api(tags ="定时任务相关接口API") public class QuartzWebController { @Autowired private QuartzUtil quartzUtil; @Autowired private QuartzWebService quartzWebService; @PostMapping("/add-job") @ApiOperation(value ="添加任务", notes ="添加任务", httpMethod ="POST") public Rs AddQuartz(@Valid @RequestBody CreateJobParam entity) { JobDataMap jobDataMap = getJobDataMap(entity); String exp = CronUtil.createCronExpression(2, (Integer) jobDataMap.get("minute"), (Integer) jobDataMap.get("hour"), null, (List) jobDataMap.get("weekDays"), null); boolean res = ValidCronUtil.isValidCronExpression(exp); if (!res) GlobalException.cast("参数有误!"); entity.setCron(exp); boolean result = quartzUtil.addJob(entity.getJobname(), QuartzGroupEnum.T1.getValue(), MyJob.class, entity.getStarttime(), entity.getEndtime(), entity.getCron(), jobDataMap, entity.getRoleid()); return result ? Rs.success("添加成功") : Rs.error("添加失败"); } @PostMapping("/modify-job") @ApiOperation(value ="修改任务", notes ="修改任务", httpMethod ="POST") public Rs modifyQuartz(@Valid @RequestBody UpdateJobParam entity) { JobDataMap jobDataMap = new JobDataMap(); // false || false || true if (entity.getMinute() != null || entity.getHour() != null || entity.getWeekDays() != null) { String exp = CronUtil.createCronExpression(2, entity.getMinute(), entity.getHour(), null, entity.getWeekDays(), null); boolean res = ValidCronUtil.isValidCronExpression(exp); if (!res) GlobalException.cast("参数有误!"); entity.setCron(exp); jobDataMap.put("minute", entity.getMinute()); jobDataMap.put("hour", entity.getHour()); jobDataMap.put("weekDays", entity.getWeekDays()); } if (entity.getRoleid() != null) { jobDataMap.put("roleId", entity.getRoleid()); } if (entity.getSendMessage() != null) { jobDataMap.put("megContent", entity.getSendMessage()); } if (entity.getDayType() != null) { jobDataMap.put("dayType", entity.getDayType() == null ? null : 1); } boolean result = quartzUtil.modifyJobTime(entity.getJobname(), QuartzGroupEnum.T1.getValue(), entity.getStarttime(), entity.getEndtime(), entity.getCron(), entity.getId(), jobDataMap, entity.getRoleid()); return result ? Rs.success("修改成功") : Rs.success("修改失败"); } @PostMapping("/cancel-job") @ApiOperation(value ="停止任务", notes ="停止任务", httpMethod ="POST") public Rs cancelTimeQuartz(@RequestBody QuartzEntity entity) { boolean result = quartzUtil.cancelJob(entity.getJobname(), QuartzGroupEnum.T1.getValue()); return result ? Rs.success("操作成功") : Rs.success("操作失败"); } @GetMapping("/get-all-jobs") @ApiOperation(value ="查询正在执行的任务", notes ="查询正在执行的任务", httpMethod ="GET") public Rs getAllJobs() throws SchedulerException { return Rs.success(quartzUtil.getAllJobs()); } @GetMapping("/query-all-job") @ApiOperation(value ="查询所有创建的任务", notes ="查询所有创建的任务", httpMethod ="GET") public Rs getAllJob() { return Rs.success(quartzWebService.queryJobAll()); } private JobDataMap getJobDataMap(CreateJobParam entity) { JobDataMap jobDataMap = new JobDataMap(); jobDataMap.put("megContent", entity.getSendMessage()); jobDataMap.put("roleId", entity.getRoleid()); jobDataMap.put("dayType", entity.getDayType() == null ? null : 1); jobDataMap.put("minute", entity.getMinute()); jobDataMap.put("hour", entity.getHour()); jobDataMap.put("weekDays", entity.getWeekDays()); return jobDataMap; } } Application启动类配置 /** * @author Laity */ @MapperScan("com.laity.control_core.dao") @ComponentScan({"com.laity.*"}) @EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) @SpringBootApplication() // exclude = {SecurityAutoConfiguration.class, SecurityFilterAutoConfiguration.class} public class ControlApplication implements ApplicationRunner { @Resource private QuartzUtil quartzUtil; public static void main(String[] args) { SpringApplication.run(ControlApplication.class, args); } @Override public void run(ApplicationArguments args) throws Exception { quartzUtil.recoveryAllJob(); } } MyJob定时业务 /** * @author: Laity * @Project: JavaLaity */ @Component("MysqlJob") public class MysqlJob implements Job { protected final Logger logger = LoggerFactory.getLogger(this.getClass()); @Override public void execute(JobExecutionContext context) throws JobExecutionException { JobKey key = context.getJobDetail().getKey(); JobDataMap jobDataMap = context.getJobDetail().getJobDataMap(); System.out.println(key.getName()); String megContent = (String) context.getTrigger().getJobDataMap().get("megContent"); Integer roleId = (Integer) context.getTrigger().getJobDataMap().get("roleId"); Integer dayType = (Integer) context.getTrigger().getJobDataMap().get("dayType"); // 需要使用ServiceBean => ARR arr = (ARR) SpringContextJobUtil.getBean("arrWebService"); …… } SchedulerListener监听器 /** * @author: Laity * @Project: JavaLaity * @Description: 全局监听器 - 接收所有的Trigger/Job的事件通知 */ public class MyJobListener implements SchedulerListener { @Override public void jobScheduled(Trigger trigger) { // 用于部署JobDetail时调用 String jobName = trigger.getJobKey().getName(); System.out.println("用于部署JobDetail时调用==>"+ jobName); } @Override public void jobUnscheduled(TriggerKey triggerKey) { // 用于卸载JobDetail时调用 System.out.println(triggerKey +"完成卸载"); } @Override public void triggerFinalized(Trigger trigger) { // 当endTime到了就会执行 System.out.println("触发器被移除:"+ trigger.getJobKey().getName()); QuartzWebService quartzService = (QuartzWebService) SpringContextJobUtil.getBean("quartzService"); quartzService.modifyTaskStatus(trigger.getJobKey().getName(),"2"); } @Override public void triggerPaused(TriggerKey triggerKey) { System.out.println(triggerKey +"正在被暂停"); } @Override public void triggersPaused(String s) { // s = triggerGroup System.out.println("触发器组:"+ s +",正在被暂停"); } @Override public void triggerResumed(TriggerKey triggerKey) { System.out.println(triggerKey +"正在从暂停中恢复"); } @Override public void triggersResumed(String s) { System.out.println("触发器组:"+ s +",正在从暂停中恢复"); } @Override public void jobAdded(JobDetail jobDetail) { System.out.println(jobDetail.getKey() +"=>已添加工作任务"); } @Override public void jobDeleted(JobKey jobKey) { System.out.println(jobKey +"=> 已删除该工作任务"); } @Override public void jobPaused(JobKey jobKey) { System.out.println(jobKey +"=> 工作任务正在被暂停"); } @Override public void jobsPaused(String s) { System.out.println("工作任务组:"+ s +",正在被暂停"); } @Override public void jobResumed(JobKey jobKey) { System.out.println(jobKey +"jobKey正在从暂停中恢复"); } @Override public void jobsResumed(String s) { System.out.println("工作任务组:"+ s +",正在从暂停中恢复"); } @Override public void schedulerError(String s, SchedulerException e) { } @Override public void schedulerInStandbyMode() { } @Override public void schedulerStarted() { } @Override public void schedulerStarting() { System.out.println("=============================开启监听==========================="); } @Override public void schedulerShutdown() { } @Override public void schedulerShuttingdown() { } @Override public void schedulingDataCleared() { } } 监听器使用
scheduler.scheduleJob(jobDetail, trigger); scheduler.getListenerManager().addSchedulerListener(new MyJobListener()); // 使用监听器 封装接收前端Param /** * @author: Laity * @Project: JavaLaity */ @Data @ApiModel(value ="创建定时任务") @Accessors(chain = true) public class CreateJobParam { @ApiModelProperty(value ="任务名称") @NotBlank(message ="任务名称不能为空") private String jobname; @ApiModelProperty(value ="开始时间") private Date starttime; @ApiModelProperty(value ="结束时间") private Date endtime; // @Ignore @ApiModelProperty(value ="cron表达式") private String cron; @ApiModelProperty(value ="角色id") @NotNull(message ="角色ID不能为空") private Integer roleid; @ApiModelProperty(value ="消息内容") @NotBlank(message ="消息内容不能为空") private String sendMessage; @ApiModelProperty(value ="因为有的消息是发给昨日的某人,所以设立此标识符,正常的不用传值,非正常:1") private Integer dayType; @ApiModelProperty(value ="指定分钟 0-60") @Max(60) @Min(0) private Integer minute; @Max(24) @Min(0) @ApiModelProperty(value ="指定小时 0-24") private Integer hour; @ApiModelProperty(value ="星期列表: 1/星期天、2/星期一、3/...、7/星期六") private List weekDays; } 测试
说明可根据自己的需求自行配置其余配置:多线程、Reids缓存、MySQL、Quartz其余配置等
解决问题修改Quartz中的JobDetailMap数据因为我在JobDetailMap中放入了一些数据,但是修改之后数据不发生变化
解决思路:
最早写法是:
// addjob中存jobDetail JobDetail jobDetail = JobBuilder.newJob(jobClass). withIdentity(name, group).setJobData(jobDataMap).build(); TriggerBuilder triggerBuilder = newTrigger(); 更改后写法:
// 在构建Trigger实例时使用.usingJobData()方法实现 TriggerBuilder triggerBuilder = newTrigger(); triggerBuilder.withIdentity(name, group); triggerBuilder.startAt(toStartDate(startTime)); triggerBuilder.endAt(toEndDate(endTime)); triggerBuilder.usingJobData(jobDataMap); // usingJobData传入jobDataMap 其中出现的问题:停止服务,查询配置不一致
存数据库最初写法:
// getAllJobs entity.setJobDataMapJson(JSONUtil.toJsonStr(jobDetail.getJobDataMap())); 现在写法:
// oldTrigger需要自己构建 entity.setJobdatamapjson(JSONUtil.toJsonStr(oldTrigger.getJobDataMap())); 那么任务job的数据呢?
最早的写法:获取jobDataMap数据
String megContent = (String) context.getJobDetail().getJobDataMap().get("megContent"); Integer roleId = (Integer) context.getJobDetail().getJobDataMap().get("roleId"); Integer dayType = (Integer) context.getJobDetail().getJobDataMap().get("dayType"); 期间也断点调试使用过其它数据获取方式
String megContent1 = (String) jobDataMap.get("megContent"); Integer roleId1 = (Integer) jobDataMap.get("roleId"); Integer dayType1 = (Integer) jobDataMap.get("dayType"); 最终实现写法:数据不论是临时修改还是怎么都可以实时更新
String megContent = (String) context.getTrigger().getJobDataMap().get("megContent"); Integer roleId = (Integer) context.getTrigger().getJobDataMap().get("roleId"); Integer dayType = (Integer) context.getTrigger().getJobDataMap().get("dayType"); 昨日之深渊,今日之浅谈;我是Laity,正在前行的Laity。