前言
之前写过一篇spring整合quartz的过程的文章,但是配置比较繁琐,而且持久化脚本需要手动生成到数据库,比较笨拙;
现在大多数项目都使用了springboot,所以本文主要讲springboot与quartz的完美整合,简化配置、持久化数据并自定义quartz数据源。
正文
一、增加依赖
我们使用的spring-boot-starter-quartz,所以不用显示指定版本号
<!--quartz相关依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
二、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: always
spring.quartz.job-store-type: jdbc
项目启动后生成的表信息
三、自定义数据源
其实到第二部配置就已经结束了,但是很多时候,我们希望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();
}
注意:
一定要增加主数据源,并加上@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<Map<String, Object>> queryAllJob() {
List<Map<String, Object>> jobList = null;
try {
GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
jobList = new ArrayList<Map<String, Object>>();
for (JobKey jobKey : jobKeys) {
List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
for (Trigger trigger : triggers) {
Map<String, Object> 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<Map<String, Object>> queryRunJob() {
List<Map<String, Object>> jobList = null;
try {
List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
jobList = new ArrayList<Map<String, Object>>(executingJobs.size());
for (JobExecutionContext executingJob : executingJobs) {
Map<String, Object> map = new HashMap<String, Object>();
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;
}
}
具体的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
*/
@Component
public 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
* @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
* @param date:时间
* @return
*/
public static String getCron(Date date){
String dateFormat="ss mm HH dd MM ? yyyy";
return formatDateByPattern(date,dateFormat);
}
}
五、测试
@Autowired
private QuartzService quartzService;
@PostMapping("/addjob")
@ApiOperation(value = "增加定时任务")
public void addJob() {
Map map = new HashMap(2);
map.put("id",1L);
quartzService.addJob(Job.class, "job", "test", "0/30 * * * * ?",map);
}
====================================================================
代码均已上传至本人的开源项目