前言
之前写过一篇spring整合quartz的过程的文章,但是配置比较繁琐,而且持久化脚本需要手动生成到数据库,比较笨拙;
现在大多数项目都使用了springboot,所以本文主要讲springboot与quartz的完美整合,简化配置、持久化数据并自定义quartz数据源。
![38a67fb47537205c471fcadee047b5a1.png](https://i-blog.csdnimg.cn/blog_migrate/51102261d664c046811c1cecce06dad6.jpeg)
正文
一、增加依赖
我们使用的spring-boot-starter-quartz,所以不用显示指定版本号
org.springframework.boot spring-boot-starter-quartz
二、yml配置信息
spring: # 数据库配置 datasource: type: com.alibaba.druid.pool.DruidDataSource dynamic: primary: master #设置默认的数据源或者数据源组,默认值即为master datasource: master: url: xxx driver-class-name: com.mysql.cj.jdbc.Driver username: root password: xxx quartz: url: xxx driver-class-name: com.mysql.cj.jdbc.Driver username: root password: xxx # quartz定时任务 quartz: jdbc: # 初始化Quartz表结构,项目第一次启动配置程always,然后改成never 否则已生成的job会被初始化掉 initialize-schema: never #设置quartz任务的数据持久化方式,默认是内存方式 job-store-type: jdbc properties: org: quartz: scheduler: instanceName: etlCleanScheduler instanceId: AUTO jobStore: class: org.quartz.impl.jdbcjobstore.JobStoreTX driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate tablePrefix: QRTZ_ #Quartz表前缀 isClustered: true clusterCheckinInterval: 10000 useProperties: false threadPool: class: org.quartz.simpl.SimpleThreadPool #线程数 一个任务使用一个线程 threadCount: 100 threadPriority: 5 threadsInheritContextClassLoaderOfInitializingThread: true
自定生成表结构,需要配置如下信息:
spring.quartz.jdbc.initialize-schema: alwaysspring.quartz.job-store-type: jdbc
项目启动后生成的表信息
![c26f729ae8bcacb10e4d8b8dde5faa3e.png](https://i-blog.csdnimg.cn/blog_migrate/2a7641add24949d32f6f6aaaf6094c1f.jpeg)
三、自定义数据源
其实到第二部配置就已经结束了,但是很多时候,我们希望quartz的数据源和项目的业务数据源是分离的,那我们需要再配置下数据源。
在启动类下增加以下代码:
/** * 主数据源 * @return */@Primary@Bean@ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.master")public DruidDataSource druidDataSource() { return new DruidDataSource();}/** * 配置Quartz独立数据源的配置 */@Bean@QuartzDataSource@ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.quartz")public DataSource quartzDataSource(){ return new DruidDataSource();}
![1c96bb18ae2c1c594a5e7df9ec01e575.gif](https://i-blog.csdnimg.cn/blog_migrate/193baf41795c24988fd4257c0ce45670.gif)
注意:
![438e8e6ba25d81c690569a66aad393ea.png](https://i-blog.csdnimg.cn/blog_migrate/8f881bdd196288c06731853f88d8deb1.jpeg)
一定要增加主数据源,并加上@primary注解,不然业务用的数据源会切换到quartz数据源
四、定时任务逻辑封装
QuartzService定时器操作业务类,需要增加数据源切换注解。
/** * quartz逻辑 * @author gourd */@Service@DS("quartz")public class QuartzServiceImpl implements QuartzService { @Autowired private Scheduler scheduler; @PostConstruct public void startScheduler() { try { scheduler.start(); } catch (SchedulerException e) { e.printStackTrace(); } } /** * 增加一个job * * @param jobClass * 任务实现类 * @param jobName * 任务名称 * @param jobGroupName * 任务组名 * @param jobTime * 时间表达式 (这是每隔多少秒为一次任务) * @param jobTimes * 运行的次数 (<0:表示不限次数) * @param jobData * 参数 */ @Override public void addJob(Class extends QuartzJobBean> jobClass, String jobName, String jobGroupName, int jobTime, int jobTimes, Map jobData) { try { // 任务名称和组构成任务key JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName) .build(); // 设置job参数 if(jobData!= null && jobData.size()>0){ jobDetail.getJobDataMap().putAll(jobData); } // 使用simpleTrigger规则 Trigger trigger = null; if (jobTimes < 0) { trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName) .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(1).withIntervalInSeconds(jobTime)) .startNow().build(); } else { trigger = TriggerBuilder .newTrigger().withIdentity(jobName, jobGroupName).withSchedule(SimpleScheduleBuilder .repeatSecondlyForever(1).withIntervalInSeconds(jobTime).withRepeatCount(jobTimes)) .startNow().build(); } scheduler.scheduleJob(jobDetail, trigger); } catch (SchedulerException e) { e.printStackTrace(); } } /** * 增加一个job * * @param jobClass * 任务实现类 * @param jobName * 任务名称(建议唯一) * @param jobGroupName * 任务组名 * @param jobTime * 时间表达式 (如:0/5 * * * * ? ) * @param jobData * 参数 */ @Override public void addJob(Class extends QuartzJobBean> jobClass, String jobName, String jobGroupName, String jobTime, Map jobData) { try { // 创建jobDetail实例,绑定Job实现类 // 指明job的名称,所在组的名称,以及绑定job类 // 任务名称和组构成任务key JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName) .build(); // 设置job参数 if(jobData!= null && jobData.size()>0){ jobDetail.getJobDataMap().putAll(jobData); } // 定义调度触发规则 // 使用cornTrigger规则 // 触发器key Trigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName) .startAt(DateBuilder.futureDate(1, IntervalUnit.SECOND)) .withSchedule(CronScheduleBuilder.cronSchedule(jobTime)).startNow().build(); // 把作业和触发器注册到任务调度中 scheduler.scheduleJob(jobDetail, trigger); } catch (Exception e) { e.printStackTrace(); } } /** * 修改 一个job的 时间表达式 * * @param jobName * @param jobGroupName * @param jobTime */ @Override public void updateJob(String jobName, String jobGroupName, String jobTime) { try { TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName); CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); trigger = trigger.getTriggerBuilder().withIdentity(triggerKey) .withSchedule(CronScheduleBuilder.cronSchedule(jobTime)).build(); // 重启触发器 scheduler.rescheduleJob(triggerKey, trigger); } catch (SchedulerException e) { e.printStackTrace(); } } /** * 删除任务一个job * * @param jobName * 任务名称 * @param jobGroupName * 任务组名 */ @Override public void deleteJob(String jobName, String jobGroupName) { try { scheduler.deleteJob(new JobKey(jobName, jobGroupName)); } catch (Exception e) { e.printStackTrace(); } } /** * 暂停一个job * * @param jobName * @param jobGroupName */ @Override public void pauseJob(String jobName, String jobGroupName) { try { JobKey jobKey = JobKey.jobKey(jobName, jobGroupName); scheduler.pauseJob(jobKey); } catch (SchedulerException e) { e.printStackTrace(); } } /** * 恢复一个job * * @param jobName * @param jobGroupName */ @Override public void resumeJob(String jobName, String jobGroupName) { try { JobKey jobKey = JobKey.jobKey(jobName, jobGroupName); scheduler.resumeJob(jobKey); } catch (SchedulerException e) { e.printStackTrace(); } } /** * 立即执行一个job * * @param jobName * @param jobGroupName */ @Override public void runAJobNow(String jobName, String jobGroupName) { try { JobKey jobKey = JobKey.jobKey(jobName, jobGroupName); scheduler.triggerJob(jobKey); } catch (SchedulerException e) { e.printStackTrace(); } } /** * 获取所有计划中的任务列表 * * @return */ @Override public List> queryAllJob() { List> jobList = null; try { GroupMatcher matcher = GroupMatcher.anyJobGroup(); Set jobKeys = scheduler.getJobKeys(matcher); jobList = new ArrayList>(); for (JobKey jobKey : jobKeys) { List extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey); for (Trigger trigger : triggers) { Map map = new HashMap<>(); map.put("jobName", jobKey.getName()); map.put("jobGroupName", jobKey.getGroup()); map.put("description", "触发器:" + trigger.getKey()); Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey()); map.put("jobStatus", triggerState.name()); if (trigger instanceof CronTrigger) { CronTrigger cronTrigger = (CronTrigger) trigger; String cronExpression = cronTrigger.getCronExpression(); map.put("jobTime", cronExpression); } jobList.add(map); } } } catch (SchedulerException e) { e.printStackTrace(); } return jobList; } /** * 获取所有正在运行的job * * @return */ @Override public List> queryRunJob() { List> jobList = null; try { List executingJobs = scheduler.getCurrentlyExecutingJobs(); jobList = new ArrayList>(executingJobs.size()); for (JobExecutionContext executingJob : executingJobs) { Map map = new HashMap(); JobDetail jobDetail = executingJob.getJobDetail(); JobKey jobKey = jobDetail.getKey(); Trigger trigger = executingJob.getTrigger(); map.put("jobName", jobKey.getName()); map.put("jobGroupName", jobKey.getGroup()); map.put("description", "触发器:" + trigger.getKey()); Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey()); map.put("jobStatus", triggerState.name()); if (trigger instanceof CronTrigger) { CronTrigger cronTrigger = (CronTrigger) trigger; String cronExpression = cronTrigger.getCronExpression(); map.put("jobTime", cronExpression); } jobList.add(map); } } catch (SchedulerException e) { e.printStackTrace(); } return jobList; }}
![1c96bb18ae2c1c594a5e7df9ec01e575.gif](https://i-blog.csdnimg.cn/blog_migrate/193baf41795c24988fd4257c0ce45670.gif)
具体的Job类
import org.quartz.JobDataMap;import org.quartz.JobExecutionContext;import org.quartz.JobExecutionException;import org.springframework.scheduling.quartz.QuartzJobBean;import org.springframework.stereotype.Component;import java.util.Date;/** * job触发时间 * @author gourd */@Componentpublic class Job extends QuartzJobBean { @Override protected void executeInternal(JobExecutionContext arg0) throws JobExecutionException { System.out.println(new Date() + " job执行"); // 获取参数 JobDataMap jobDataMap = arg0.getJobDetail().getJobDataMap(); // 业务逻辑 ... }}
cron工具类,将date 转换成cron格式
import java.text.SimpleDateFormat;import java.util.Date; public class QuartzCronDateUtils {/*** * 功能描述:日期转换cron表达式时间格式 * @param date * @param dateFormat : e.g:yyyy-MM-dd HH:mm:ss * @return */ public static String formatDateByPattern(Date date,String dateFormat){ SimpleDateFormat sdf = new SimpleDateFormat(dateFormat); String formatTimeStr = null; if (date != null) { formatTimeStr = sdf.format(date); } return formatTimeStr; } /*** * convert Date to cron ,eg. "14 01 17 22 07 ? 2017" * @param date:时间点 * @return */ public static String getCron(Date date){ String dateFormat="ss mm HH dd MM ? yyyy"; return formatDateByPattern(date,dateFormat); } }
五、测试
@Autowiredprivate QuartzService quartzService;@PostMapping("/addjob")@ApiOperation(value = "增加定时任务")public void addJob() { Map map = new HashMap(2); map.put("id",1L); // 先删除,再新增加 quartzService.deleteJob("job", "test"); quartzService.addJob(Job.class, "job", "test", "0/30 * * * * ?",map);}