一、QuartJob简介
1、一句话描述
Quartz是一个完全由java编写的开源作业调度框架,形式简易,功能强大。
2、核心API
(1)、Scheduler
代表一个 Quartz 的独立运行容器,Scheduler 将 Trigger 绑定到特定 JobDetail, 这样当 Trigger 触发时, 对应的 Job 就会被调度。
(2)、Trigger
描述 Job 执行的时间触发规则。主要有 SimpleTrigger 和 CronTrigger 两个子类,通过一个 TriggerKey 唯一标识。
(3)、Job
定义一个任务,规定了任务是执行时的行为。JobExecutionContext 提供了调度器的上下文信息,Job 的数据可从 JobDataMap 中获取。
(4)、JobDetail
Quartz 在每次执行 Job 时,都重新创建一个 Job 实例,所以它不直接接受一个 Job 的实例,相反它接收一个 Job 实现类。描述 Job 的实现类及其它相关的静态信息,如 Job 名字、描述等。
二、与SpringBoot2.0 整合
1、项目结构

版本描述
1spring-boot:2.1.3.RELEASE 2quart-job:2.3.0
2、定时器配置
1import org.springframework.context.annotation.Bean; 2import org.springframework.context.annotation.Configuration; 3import org.springframework.scheduling.quartz.SchedulerFactoryBean; 4import javax.sql.DataSource; 5import java.util.Properties; 6@Configuration 7public class ScheduleConfig { 8 @Bean 9 public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) { 10 // Quartz参数配置 11 Properties prop = new Properties(); 12 // Schedule调度器的实体名字 13 prop.put("org.quartz.scheduler.instanceName", "HuskyScheduler"); 14 // 设置为AUTO时使用,默认的实现org.quartz.scheduler.SimpleInstanceGenerator是基于主机名称和时间戳生成。 15 prop.put("org.quartz.scheduler.instanceId", "AUTO"); 16 // 线程池配置 17 prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool"); 18 prop.put("org.quartz.threadPool.threadCount", "20"); 19 prop.put("org.quartz.threadPool.threadPriority", "5"); 20 // JobStore配置:Scheduler在运行时用来存储相关的信息 21 // JDBCJobStore和JobStoreTX都使用关系数据库来存储Schedule相关的信息。 22 // JobStoreTX在每次执行任务后都使用commit或者rollback来提交更改。 23 prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX"); 24 // 集群配置:如果有多个调度器实体的话则必须设置为true 25 prop.put("org.quartz.jobStore.isClustered", "true"); 26 // 集群配置:检查集群下的其他调度器实体的时间间隔 27 prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000"); 28 // 设置一个频度(毫秒),用于实例报告给集群中的其他实例 29 prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1"); 30 // 触发器触发失败后再次触犯的时间间隔 31 prop.put("org.quartz.jobStore.misfireThreshold", "12000"); 32 // 数据库表前缀 33 prop.put("org.quartz.jobStore.tablePrefix", "qrtz_"); 34 // 从 LOCKS 表查询一行并对这行记录加锁的 SQL 语句 35 prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?"); 36 37 // 定时器工厂配置 38 SchedulerFactoryBean factory = new SchedulerFactoryBean(); 39 factory.setDataSource(dataSource); 40 factory.setQuartzProperties(prop); 41 factory.setSchedulerName("HuskyScheduler"); 42 factory.setStartupDelay(30); 43 factory.setApplicationContextSchedulerContextKey("applicationContextKey"); 44 // 可选,QuartzScheduler 启动时更新己存在的Job 45 factory.setOverwriteExistingJobs(true); 46 // 设置自动启动,默认为true 47 factory.setAutoStartup(true); 48 return factory; 49 } 50}
3、定时器管理工具
1import com.quart.job.entity.ScheduleJobBean; 2import org.quartz.*; 3/** 4 * 定时器工具类 5 */ 6public class ScheduleUtil { 7 private ScheduleUtil (){} 8 private static final String SCHEDULE_NAME = "HUSKY_" ; 9 /** 10 * 触发器 KEY 11 */ 12 public static TriggerKey getTriggerKey(Long jobId){ 13 return TriggerKey.triggerKey(SCHEDULE_NAME+jobId) ; 14 } 15 /** 16 * 定时器 Key 17 */ 18 public static JobKey getJobKey (Long jobId){ 19 return JobKey.jobKey(SCHEDULE_NAME+jobId) ; 20 } 21 /** 22 * 表达式触发器 23 */ 24 public static CronTrigger getCronTrigger (Scheduler scheduler,Long jobId){ 25 try { 26 return (CronTrigger)scheduler.getTrigger(getTriggerKey(jobId)) ; 27 } catch (SchedulerException e){ 28 throw new RuntimeException("getCronTrigger Fail",e) ; 29 } 30 } 31 /** 32 * 创建定时器 33 */ 34 public static void createJob (Scheduler scheduler, ScheduleJobBean scheduleJob){ 35 try { 36 // 构建定时器 37 JobDetail jobDetail = JobBuilder.newJob(TaskJobLog.class).withIdentity(getJobKey(scheduleJob.getJobId())).build() ; 38 CronScheduleBuilder scheduleBuilder = CronScheduleBuilder 39 .cronSchedule(scheduleJob.getCronExpression()) 40 .withMisfireHandlingInstructionDoNothing() ; 41 CronTrigger trigger = TriggerBuilder.newTrigger() 42 .withIdentity(getTriggerKey(scheduleJob.getJobId())) 43 .withSchedule(scheduleBuilder).build() ; 44 jobDetail.getJobDataMap().put(ScheduleJobBean.JOB_PARAM_KEY,scheduleJob); 45 scheduler.scheduleJob(jobDetail,trigger) ; 46 // 如果该定时器处于暂停状态 47 if (scheduleJob.getStatus() == 1){ 48 pauseJob(scheduler,scheduleJob.getJobId()) ; 49 } 50 } catch (SchedulerException e){ 51 throw new RuntimeException("createJob Fail",e) ; 52 } 53 } 54 /** 55 * 更新定时任务 56 */ 57 public static void updateJob(Scheduler scheduler, ScheduleJobBean scheduleJob) { 58 try { 59 // 构建定时器 60 TriggerKey triggerKey = getTriggerKey(scheduleJob.getJobId()); 61 CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression()) 62 .withMisfireHandlingInstructionDoNothing(); 63 CronTrigger trigger = getCronTrigger(scheduler, scheduleJob.getJobId()); 64 trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build(); 65 trigger.getJobDataMap().put(ScheduleJobBean.JOB_PARAM_KEY, scheduleJob); 66 scheduler.rescheduleJob(triggerKey, trigger); 67 // 如果该定时器处于暂停状态 68 if(scheduleJob.getStatus() == 1){ 69 pauseJob(scheduler, scheduleJob.getJobId()); 70 } 71 } catch (SchedulerException e) { 72 throw new RuntimeException("updateJob Fail",e) ; 73 } 74 } 75 /** 76 * 停止定时器 77 */ 78 public static void pauseJob (Scheduler scheduler,Long jobId){ 79 try { 80 scheduler.pauseJob(getJobKey(jobId)); 81 } catch (SchedulerException e){ 82 throw new RuntimeException("pauseJob Fail",e) ; 83 } 84 } 85 /** 86 * 恢复定时器 87 */ 88 public static void resumeJob (Scheduler scheduler,Long jobId){ 89 try { 90 scheduler.resumeJob(getJobKey(jobId)); 91 } catch (SchedulerException e){ 92 throw new RuntimeException("resumeJob Fail",e) ; 93 } 94 } 95 /** 96 * 删除定时器 97 */ 98 public static void deleteJob (Scheduler scheduler,Long jobId){ 99 try { 100 scheduler.deleteJob(getJobKey(jobId)); 101 } catch (SchedulerException e){ 102 throw new RuntimeException("deleteJob Fail",e) ; 103 } 104 } 105 /** 106 * 执行定时器 107 */ 108 public static void run (Scheduler scheduler, ScheduleJobBean scheduleJob){ 109 try { 110 JobDataMap dataMap = new JobDataMap() ; 111 dataMap.put(ScheduleJobBean.JOB_PARAM_KEY,scheduleJob); 112 scheduler.triggerJob(getJobKey(scheduleJob.getJobId()),dataMap); 113 } catch (SchedulerException e){ 114 throw new RuntimeException("run Fail",e) ; 115 } 116 } 117}
4、定时器执行和日志
1import com.quart.job.entity.ScheduleJobBean; 2import com.quart.job.entity.ScheduleJobLogBean; 3import com.quart.job.service.ScheduleJobLogService; 4import org.quartz.JobExecutionContext; 5import org.slf4j.Logger; 6import org.slf4j.LoggerFactory; 7import org.springframework.scheduling.quartz.QuartzJobBean; 8import java.lang.reflect.Method; 9import java.util.Date; 10/** 11 * 定时器执行日志记录 12 */ 13public class TaskJobLog extends QuartzJobBean { 14 15 private static final Logger LOG = LoggerFactory.getLogger(TaskJobLog.class) ; 16 17 @Override 18 protected void executeInternal(JobExecutionContext context) { 19 ScheduleJobBean jobBean = (ScheduleJobBean)context.getMergedJobDataMap().get(ScheduleJobBean.JOB_PARAM_KEY) ; 20 ScheduleJobLogService scheduleJobLogService = (ScheduleJobLogService)SpringContextUtil.getBean("scheduleJobLogService") ; 21 // 定时器日志记录 22 ScheduleJobLogBean logBean = new ScheduleJobLogBean () ; 23 logBean.setJobId(jobBean.getJobId()); 24 logBean.setBeanName(jobBean.getBeanName()); 25 logBean.setParams(jobBean.getParams()); 26 logBean.setCreateTime(new Date()); 27 long beginTime = System.currentTimeMillis() ; 28 try { 29 // 加载并执行定时器的 run 方法 30 Object target = SpringContextUtil.getBean(jobBean.getBeanName()); 31 Method method = target.getClass().getDeclaredMethod("run", String.class); 32 method.invoke(target, jobBean.getParams()); 33 long executeTime = System.currentTimeMillis() - beginTime; 34 logBean.setTimes((int)executeTime); 35 logBean.setStatus(0); 36 LOG.info("定时器 === >> "+jobBean.getJobId()+"执行成功,耗时 === >> " + executeTime); 37 } catch (Exception e){ 38 // 异常信息 39 long executeTime = System.currentTimeMillis() - beginTime; 40 logBean.setTimes((int)executeTime); 41 logBean.setStatus(1); 42 logBean.setError(e.getMessage()); 43 } finally { 44 scheduleJobLogService.insert(logBean) ; 45 } 46 } 47}
三、定时器服务封装
1、定时器初始化
1@Service 2public class ScheduleJobServiceImpl implements ScheduleJobService { 3 4 @Resource 5 private Scheduler scheduler ; 6 @Resource 7 private ScheduleJobMapper scheduleJobMapper ; 8 9 /** 10 * 定时器初始化 11 */ 12 @PostConstruct 13 public void init (){ 14 ScheduleJobExample example = new ScheduleJobExample() ; 15 List<ScheduleJobBean> scheduleJobBeanList = scheduleJobMapper.selectByExample(example) ; 16 for (ScheduleJobBean scheduleJobBean : scheduleJobBeanList) { 17 CronTrigger cronTrigger = ScheduleUtil.getCronTrigger(scheduler,scheduleJobBean.getJobId()) ; 18 if (cronTrigger == null){ 19 ScheduleUtil.createJob(scheduler,scheduleJobBean); 20 } else { 21 ScheduleUtil.updateJob(scheduler,scheduleJobBean); 22 } 23 } 24 } 25}
2、添加定时器
1@Override 2@Transactional(rollbackFor = Exception.class) 3public int insert(ScheduleJobBean record) { 4 ScheduleUtil.createJob(scheduler,record); 5 return scheduleJobMapper.insert(record); 6}
3、立即执行一次定时器
1@Override 2@Transactional(rollbackFor = Exception.class) 3public void run(Long jobId) { 4 ScheduleJobBean scheduleJobBean = scheduleJobMapper.selectByPrimaryKey(jobId) ; 5 ScheduleUtil.run(scheduler,scheduleJobBean); 6}
4、更新定时器
1@Override 2@Transactional(rollbackFor = Exception.class) 3public int updateByPrimaryKeySelective(ScheduleJobBean record) { 4 ScheduleUtil.updateJob(scheduler,record); 5 return scheduleJobMapper.updateByPrimaryKeySelective(record); 6}
5、停止定时器
1@Override 2@Transactional(rollbackFor = Exception.class) 3public void pauseJob(Long jobId) { 4 ScheduleJobBean scheduleJobBean = scheduleJobMapper.selectByPrimaryKey(jobId) ; 5 ScheduleUtil.pauseJob(scheduler,jobId); 6 scheduleJobBean.setStatus(1); 7 scheduleJobMapper.updateByPrimaryKeySelective(scheduleJobBean) ; 8}
6、恢复定时器
1@Override 2@Transactional(rollbackFor = Exception.class) 3public void resumeJob(Long jobId) { 4 ScheduleJobBean scheduleJobBean = scheduleJobMapper.selectByPrimaryKey(jobId) ; 5 ScheduleUtil.resumeJob(scheduler,jobId); 6 scheduleJobBean.setStatus(0); 7 scheduleJobMapper.updateByPrimaryKeySelective(scheduleJobBean) ; 8}
7、删除定时器
1@Override 2@Transactional(rollbackFor = Exception.class) 3public void delete(Long jobId) { 4 ScheduleUtil.deleteJob(scheduler, jobId); 5 scheduleJobMapper.deleteByPrimaryKey(jobId) ; 6}
四、配置一个测试的定时器
1、定时接口封装
1public interface TaskService { 2 void run(String params); 3}
2、测试定时器
1@Component("getTimeTask") 2public class GetTimeTask implements TaskService { 3 private static final Logger LOG = LoggerFactory.getLogger(GetTimeTask.class.getName()) ; 4 private static final SimpleDateFormat format = 5 new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") ; 6 @Override 7 public void run(String params) { 8 LOG.info("Params === >> " + params); 9 LOG.info("当前时间::::"+format.format(new Date())); 10 } 11}
五、源代码
1GitHub地址:知了一笑 2https://github.com/cicadasmile/middle-ware-parent 3码云地址:知了一笑 4https://gitee.com/cicadasmile/middle-ware-parent