SpringBoot整合Quartz [整合完整版......]
环境:
java1.8
SpringBoot 2.1.1.RELEASE
Quartz 2.3.0
Maven 3.3.9
开始参考网上许多的整合,很多都不太完整
- 导入依赖包
- 配置Quartz类
- 实现监听器
- 自定义任务
目录结构
导入依赖包
<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-devtools</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
配置Quartz类
package com.quartz.zy.quartz.config; import com.quartz.zy.quartz.job.MyJob; import com.quartz.zy.quartz.listener.TriggerListenerLogMonitor; import org.quartz.*; import org.quartz.ee.servlet.QuartzInitializerListener; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.quartz.SchedulerFactoryBean; import java.io.IOException; /** * @author zhangyi * @date 2018/12/20 10:59 */ @Configuration public class QuartzConfig { @Bean(name = "SchedulerFactory") public SchedulerFactoryBean schedulerFactoryBean() throws IOException { SchedulerFactoryBean factory = new SchedulerFactoryBean(); //用于quartz集群,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了 factory.setOverwriteExistingJobs(true); //任务调度监听类 factory.setGlobalTriggerListeners(triggerListenerLogMonitor()); return factory; }
/** * quartz初始化监听器 * * @return */ @Bean public QuartzInitializerListener executorListener() { return new QuartzInitializerListener(); } @Bean public TriggerListenerLogMonitor triggerListenerLogMonitor() { return new TriggerListenerLogMonitor(); }
/** * 新增一个定时任务(测试) * @param scheduler * @author sujin */ private void addmyTestJob(Scheduler scheduler){ //是否开始 String startJob = "true"; String jobName = "DATAMART_SYNC"; String jobGroup = "DATAMART_SYNC"; //定时的时间设置 String cron = "0 0/1 * * * ?"; String className = MyJob.class.getName(); if (startJob != null && startJob.equals("true")) { addCommonCronJob(jobName, jobGroup, cron, scheduler, className); } else { deleteCommonJob(jobName, jobGroup, scheduler); } } private void deleteCommonJob(String jobName, String jobGroup, Scheduler scheduler) { JobKey jobKey = JobKey.jobKey(jobName, jobGroup); try { //先暂停任务 scheduler.pauseJob(jobKey); //再删除任务 scheduler.deleteJob(jobKey); } catch (SchedulerException e) { e.printStackTrace(); } } /** * 添加一些定时任务,如日志清理任务 */ private void addCommonCronJob(String jobName, String jobGroup, String cron, Scheduler scheduler, String className) { try { TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup); //任务触发 Trigger checkExist = (CronTrigger) scheduler.getTrigger(triggerKey); if (checkExist == null) { JobDetail jobDetail = null; jobDetail = JobBuilder.newJob((Class<? extends Job>) Class.forName(className)) //当Quartz服务被中止后,再次启动或集群中其他机器接手任务时会尝试恢复执行之前未完成的所有任务 .requestRecovery(true) .withIdentity(jobName, jobGroup) .build(); jobDetail.getJobDataMap().put("jobName", jobName); jobDetail.getJobDataMap().put("jobGroup", jobGroup); CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron); /*withMisfireHandlingInstructionDoNothing ——不触发立即执行 ——等待下次Cron触发频率到达时刻开始按照Cron频率依次执行 withMisfireHandlingInstructionIgnoreMisfires ——以错过的第一个频率时间立刻开始执行 ——重做错过的所有频率周期后 ——当下一次触发频率发生时间大于当前时间后,再按照正常的Cron频率依次执行 withMisfireHandlingInstructionFireAndProceed ——以当前时间为触发频率立刻触发一次执行 ——然后按照Cron频率依次执行*/ Trigger trigger = TriggerBuilder.newTrigger() .withIdentity(jobName, jobGroup) .withSchedule(cronScheduleBuilder.withMisfireHandlingInstructionIgnoreMisfires()) .build(); scheduler.scheduleJob(jobDetail, trigger); } else { scheduler.deleteJob(JobKey.jobKey(jobName, jobGroup)); addCommonCronJob(jobName, jobGroup, cron, scheduler, className); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SchedulerException e) { e.printStackTrace(); } } }
实现监听器
package com.quartz.zy.quartz.listener; import org.quartz.*; import org.springframework.stereotype.Component; /** * 任务调度监听器 * * @author zhangyi * @date 2018/12/20 11:06 */ @Component public class TriggerListenerLogMonitor implements TriggerListener { @Override public String getName() { return "TriggerListenerLogMonitor"; } /** * // 当与监听器相关联的 Trigger 被触发,Job 上的 execute() 方法将要被执行时, * Scheduler 就调用这个方法。 * * @param trigger * @param context */ @Override public void triggerFired(Trigger trigger, JobExecutionContext context) { System.out.println("TriggerListenerLogMonitor类:" + context.getTrigger().getKey().getName() + " 被执行"); } /** * 在 Trigger 触发后,Job 将要被执行时由 Scheduler 调用这个方法。 * TriggerListener 给了一个选择去否决 Job 的执行。 * 假如这个方法返回 true,这个 Job 将不会为此次 Trigger 触发而得到执行。 */ @Override public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) { return false; } /** * Scheduler 调用这个方法是在 Trigger 错过触发时。 * 如这个方法的 JavaDoc 所指出的,你应该关注此方法中持续时间长的逻辑: * 在出现许多错过触发的 Trigger 时, * 长逻辑会导致骨牌效应。 * 你应当保持这上方法尽量的小。 */ @Override public void triggerMisfired(Trigger trigger) { System.out.println("Job错过触发"); } /** * Trigger 被触发并且完成了 Job 的执行时,Scheduler 调用这个方法。 * 这不是说这个 Trigger 将不再触发了,而仅仅是当前 Trigger 的触发(并且紧接着的 Job 执行) 结束时。 * 这个 Trigger 也许还要在将来触发多次的。 */ @Override public void triggerComplete(Trigger trigger, JobExecutionContext context, Trigger.CompletedExecutionInstruction triggerInstructionCode) { System.out.println("Job执行完毕,Trigger完成"); } }
自定义逻辑
import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import java.util.Date; /** * @author zhangyi * @date 2018/12/20 11:04 */ public class MyJob implements Job { @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { System.out.println("我的定时任务执行,我在配置文件里面配置了1分钟执行一次"+new Date(System.currentTimeMillis())); } }
整合的时候出现一个包重读导入问题:
在application中配置,重复导入为true
spring:
main:
allow-bean-definition-overriding: true
收集资料CRON表达式: Cron表达式的格式:秒 分 时 日 月 周 年(可选)。 字段名 允许的值 允许的特殊字符 秒 0-59 , - * / 分 0-59 , - * / 小时 0-23 , - * / 日 1-31 , - * ? / L W C 月 1-12 or JAN-DEC , - * / 周几 1-7 or SUN-SAT , - * ? / L C # 年(可选字段) empty 1970-2099 , - * /
字符含义: * :代表所有可能的值。因此,“*”在Month中表示每个月,在Day-of-Month中表示每天,在Hours表示每小时 - :表示指定范围。 , :表示列出枚举值。例如:在Minutes子表达式中,“5,20”表示在5分钟和20分钟触发。 / :被用于指定增量。例如:在Minutes子表达式中,“0/15”表示从0分钟开始,每15分钟执行一次。"3/20"表示从第三分钟开始,每20分钟执行一次。和"3,23,43"(表示第3,23,43分钟触发)的含义一样。 ? :用在Day-of-Month和Day-of-Week中,指“没有具体的值”。当两个子表达式其中一个被指定了值以后,为了避免冲突,需要将另外一个的值设为“?”。例如:想在每月20日触发调度,不管20号是星期几,只能用如下写法:0 0 0 20 * ?,其中最后以为只能用“?”,而不能用“*”。 L :用在day-of-month和day-of-week字串中。它是单词“last”的缩写。它在两个子表达式中的含义是不同的。 在day-of-month中,“L”表示一个月的最后一天,一月31号,3月30号。 在day-of-week中,“L”表示一个星期的最后一天,也就是“7”或者“SAT” 如果“L”前有具体内容,它就有其他的含义了。例如:“6L”表示这个月的倒数第六天。“FRIL”表示这个月的最后一个星期五。 注意:在使用“L”参数时,不要指定列表或者范围,这样会出现问题。 W :“Weekday”的缩写。只能用在day-of-month字段。用来描叙最接近指定天的工作日(周一到周五)。例如:在day-of-month字段用“15W”指“最接近这个月第15天的工作日”,即如果这个月第15天是周六,那么触发器将会在这个月第14天即周五触发;如果这个月第15天是周日,那么触发器将会在这个月第 16天即周一触发;如果这个月第15天是周二,那么就在触发器这天触发。注意一点:这个用法只会在当前月计算值,不会越过当前月。“W”字符仅能在 day-of-month指明一天,不能是一个范围或列表。也可以用“LW”来指定这个月的最后一个工作日,即最后一个星期五。 # :只能用在day-of-week字段。用来指定这个月的第几个周几。例:在day-of-week字段用"6#3" or "FRI#3"指这个月第3个周五(6指周五,3指第3个)。如果指定的日期不存在,触发器就不会触发。
表达式例子:
0 * * * * ? 每1分钟触发一次
0 0 * * * ? 每天每1小时触发一次
0 0 10 * * ? 每天10点触发一次
0 * 14 * * ? 在每天下午2点到下午2:59期间的每1分钟触发
0 30 9 1 * ? 每月1号上午9点半
0 15 10 15 * ? 每月15日上午10:15触发
*/5 * * * * ? 每隔5秒执行一次
0 */1 * * * ? 每隔1分钟执行一次
0 0 5-15 * * ? 每天5-15点整点触发
0 0/3 * * * ? 每三分钟触发一次
0 0-5 14 * * ? 在每天下午2点到下午2:05期间的每1分钟触发
0 0/5 14 * * ? 在每天下午2点到下午2:55期间的每5分钟触发
0 0/5 14,18 * * ? 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时
0 0 10,14,16 * * ? 每天上午10点,下午2点,4点
0 0 12 ? * WED 表示每个星期三中午12点
0 0 17 ? * TUES,THUR,SAT 每周二、四、六下午五点
0 10,44 14 ? 3 WED 每年三月的星期三的下午2:10和2:44触发
0 15 10 ? * MON-FRI 周一至周五的上午10:15触发
0 0 23 L * ? 每月最后一天23点执行一次
0 15 10 L * ? 每月最后一日的上午10:15触发
0 15 10 ? * 6L 每月的最后一个星期五上午10:15触发
0 15 10 * * ? 2005 2005年的每天上午10:15触发
0 15 10 ? * 6L 2002-2005 2002年至2005年的每月的最后一个星期五上午10:15触发
0 15 10 ? * 6#3 每月的第三个星期五上午10:15触发
result
平凡是我的一个标签