springboot集成Quartz任务调度
程序员文章站
2024-03-18 17:10:22
...
概述:Quartz是功能强大的开源作业调度库,几乎可以集成到任何Java应用程序中-从最小的独立应用程序到最大的电子商务系统。Quartz可用于创建简单或复杂的计划,以执行数以万计,数以万计的工作;任务定义为标准Java组件的作业,它们实际上可以执行您可以编写的所有内容。Quartz Scheduler包含许多企业级功能,例如对JTA事务和集群的支持。
Quartz是免费使用的,并根据Apache 2.0许可获得许可。
以上是在Quartz官网上复制的。废话不多说,执行上代码。
提前准备工作,自己到quartz官网上下载源码,里面带有不同数据库版本的脚本进行初始化sql脚本
,我这快使用的是quartz-2.3.0-SNAPSHOT版本,数据库使用的postgresql 11
脚本路径为:quartz-2.3.0-distribution.tar\quartz-2.3.0-SNAPSHOT\src\org\quartz\impl\jdbcjobstore
这块根据自己使用的数据库进行选择即可。
1、pom代码
<!--quartz定时调度依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
版本是根据springboot框架自动获取的,我这块用的是2.0.4.RELEASE
2、Quartz配置类
import org.quartz.spi.JobFactory;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;
import javax.sql.DataSource;
/**
* Quartz任务调度配置器
*/
@Configuration
@EnableScheduling
public class QuartzConfiguration {
/**
* 继承org.springframework.scheduling.quartz.SpringBeanJobFactory
* 实现任务实例化方式
*/
public static class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements
ApplicationContextAware {
private transient AutowireCapableBeanFactory beanFactory;
@Override
public void setApplicationContext(final ApplicationContext context) {
beanFactory = context.getAutowireCapableBeanFactory();
}
/**
* 将job实例交给spring ioc托管
* 我们在job实例实现类内可以直接使用spring注入的调用被spring ioc管理的实例
*
* @param bundle
* @return
* @throws Exception
*/
@Override
protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
/**
* 将job实例交付给spring ioc
*/
beanFactory.autowireBean(job);
return job;
}
}
/**
* 配置任务工厂实例
*
* @return
*/
@Bean
public JobFactory jobFactory() {
/**
* 采用自定义任务工厂 整合spring实例来完成构建任务*/
AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
return jobFactory;
}
/**
* 配置任务调度器
* 使用项目数据源作为quartz数据源
*
* @param jobFactory 自定义配置任务工厂
* @param dataSource 数据源实例
* @return
* @throws Exception
*/
@Bean(destroyMethod = "destroy", autowire = Autowire.NO)
public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory, DataSource dataSource) throws Exception {
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
//将spring管理job自定义工厂交由调度器维护
schedulerFactoryBean.setJobFactory(jobFactory);
//设置覆盖已存在的任务
schedulerFactoryBean.setOverwriteExistingJobs(true);
//项目启动完成后,等待2秒后开始执行调度器初始化
schedulerFactoryBean.setStartupDelay(2);
//设置调度器自动运行
schedulerFactoryBean.setAutoStartup(true);
//设置数据源,使用与项目统一数据源
schedulerFactoryBean.setDataSource(dataSource);
//设置上下文spring bean name
schedulerFactoryBean.setApplicationContextSchedulerContextKey("applicationContext");
//设置配置文件位置
schedulerFactoryBean.setConfigLocation(new ClassPathResource("/quartz.properties"));
return schedulerFactoryBean;
}
}
3、Quartz配置文件quartz.properties
# 在集群中每个实例都必须有一个唯一的instanceId,但是应该有一个相同的instanceName【默认“QuartzScheduler”】
org.quartz.scheduler.instanceName = schedulerFactoryBean
# 只有在”org.quartz.scheduler.instanceId”设置为”AUTO”的时候才使用该属性设置
# 默认情况下,“org.quartz.simpl.SimpleInstanceIdGenerator”是基于instanceId和时间戳来自动生成的
org.quartz.scheduler.instanceId = AUTO
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
#驱动程序代表理解不同数据库系统的特定方言,许多数据库使用StdJDBCDelegate
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
#数据库中表的前缀
org.quartz.jobStore.tablePrefix = QRTZ_
# 为了指示JDBCJobStore所有的JobDataMaps中的值都是字符串,并且能以“名字-值”对的方式存储而不是以复杂对象的序列化形式存储在BLOB字段中,应该设置为true(缺省方式)
org.quartz.jobStore.useProperties = false
# 实例化ThreadPool时,使用的线程类为SimpleThreadPool
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
# 并发个数
org.quartz.threadPool.threadCount = 10
# 优先级
org.quartz.threadPool.threadPriority = 5
基本上Quartz的配置完成,下面开始些正在的实现操作。
4、TestController实现
package com.joe.oauth.quartz.controller;
import com.joe.oauth.quartz.domin.TaskScheduleVo;
import com.joe.oauth.quartz.job.TestJob1;
import com.joe.oauth.quartz.service.QuartzService;
import com.joe.oauth.quartz.util.CronUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Calendar;
@RestController
public class TestController {
@Autowired
private QuartzService quartzService;
@RequestMapping("/addjob")
public void startJob(String jobName) {
TaskScheduleVo taskScheduleVo = new TaskScheduleVo();
Calendar now = Calendar.getInstance();
taskScheduleVo.setJobType(1);
taskScheduleVo.setHour(now.get(Calendar.HOUR_OF_DAY));
taskScheduleVo.setMinute(now.get(Calendar.MINUTE)+1);//分
taskScheduleVo.setSecond(0);//秒
//组装cron表达式
String cron = CronUtil.createCronExpression(taskScheduleVo);
quartzService.addJob(TestJob1.class, jobName, "test", "0/10 * * * * ?");
}
@RequestMapping("/updatejob")
public void updatejob(String jobName) {
quartzService.updateJob(jobName, "test", "0/10 * * * * ?");
}
@RequestMapping("/deletejob")
public void deletejob(String jobName) {
quartzService.deleteJob(jobName, "test");
}
@RequestMapping("/pauseJob")
public void pauseJob(String jobName) {
quartzService.pauseJob(jobName, "test");
}
@RequestMapping("/resumeJob")
public void resumeJob(String jobName) {
quartzService.resumeJob(jobName, "test");
}
@RequestMapping("/queryAllJob")
public Object queryAllJob() {
return quartzService.queryAllJob();
}
@RequestMapping("/queryRunJob")
public Object queryRunJob() {
return quartzService.queryRunJob();
}
}
5、Job的业务层
package com.joe.oauth.quartz.job;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.PersistJobDataAfterExecution;
import org.springframework.scheduling.quartz.QuartzJobBean;
import java.util.Date;
@PersistJobDataAfterExecution //参数持久化,这块暂时没有传入参数,加上该属性不影响
@DisallowConcurrentExecution// 不允许并发执行
public class TestJob1 extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext arg0) throws JobExecutionException {
System.out.println(new Date() + " job执行");
}
}
6、QuartzService实现
package com.joe.oauth.quartz.service;
import org.quartz.*;
import org.quartz.DateBuilder.IntervalUnit;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.*;
@Service
public class 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:表示不限次数)
*/
public void addJob(Class<? extends QuartzJobBean> jobClass, String jobName, String jobGroupName, int jobTime,
int jobTimes) {
try {
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName)// 任务名称和组构成任务key
.build();
// 使用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 * * * * ? )
*/
public void addJob(Class<? extends QuartzJobBean> jobClass, String jobName, String jobGroupName, String jobTime) {
try {
// 创建jobDetail实例,绑定Job实现类
// 指明job的名称,所在组的名称,以及绑定job类
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName)// 任务名称和组构成任务key
.build();
// 定义调度触发规则
// 使用cornTrigger规则
Trigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName)// 触发器key
.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
*/
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
* 任务组名
*/
public void deleteJob(String jobName, String jobGroupName) {
try {
scheduler.deleteJob(new JobKey(jobName, jobGroupName));
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 暂停一个job
*
* @param jobName
* @param jobGroupName
*/
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
*/
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
*/
public void runAJobNow(String jobName, String jobGroupName) {
try {
JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
scheduler.triggerJob(jobKey);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
/**
* 获取所有计划中的任务列表
*
* @return
*/
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
*/
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;
}
}
7、CronUtil工具类
package com.joe.oauth.quartz.util;
import com.joe.oauth.quartz.domin.TaskScheduleVo;
public class CronUtil {
/**
*
*方法摘要:构建Cron表达式
*@param taskScheduleVo
*@return String
*/
public static String createCronExpression(TaskScheduleVo taskScheduleVo){
StringBuffer cronExp = new StringBuffer("");
if(null == taskScheduleVo.getJobType()) {
System.out.println("执行周期未配置" );//执行周期未配置
}
if (null != taskScheduleVo.getSecond()
&& null == taskScheduleVo.getMinute()
&& null == taskScheduleVo.getHour()){
//每隔几秒
if (taskScheduleVo.getJobType().intValue() == 0) {
cronExp.append("0/").append(taskScheduleVo.getSecond());
cronExp.append(" ");
cronExp.append("* ");
cronExp.append("* ");
cronExp.append("* ");
cronExp.append("* ");
cronExp.append("?");
}
}
if (null != taskScheduleVo.getSecond()
&& null != taskScheduleVo.getMinute()
&& null == taskScheduleVo.getHour()){
//每隔几分钟
if (taskScheduleVo.getJobType().intValue() == 4) {
cronExp.append("* ");
cronExp.append("0/").append(taskScheduleVo.getMinute());
cronExp.append(" ");
cronExp.append("* ");
cronExp.append("* ");
cronExp.append("* ");
cronExp.append("?");
}
}
if (null != taskScheduleVo.getSecond()
&& null != taskScheduleVo.getMinute()
&& null != taskScheduleVo.getHour()) {
//秒
cronExp.append(taskScheduleVo.getSecond()).append(" ");
//分
cronExp.append(taskScheduleVo.getMinute()).append(" ");
//小时
cronExp.append(taskScheduleVo.getHour()).append(" ");
//每天
if(taskScheduleVo.getJobType().intValue() == 1){
cronExp.append("* ");//日
cronExp.append("* ");//月
cronExp.append("?");//周
}
//按每周
else if(taskScheduleVo.getJobType().intValue() == 3){
//一个月中第几天
cronExp.append("? ");
//月份
cronExp.append("* ");
//周
Integer[] weeks = taskScheduleVo.getDayOfWeeks();
for(int i = 0; i < weeks.length; i++){
if(i == 0){
cronExp.append(weeks[i]);
} else{
cronExp.append(",").append(weeks[i]);
}
}
}
//按每月
else if(taskScheduleVo.getJobType().intValue() == 2){
//一个月中的哪几天
Integer[] days = taskScheduleVo.getDayOfMonths();
for(int i = 0; i < days.length; i++){
if(i == 0){
cronExp.append(days[i]);
} else{
cronExp.append(",").append(days[i]);
}
}
//月份
cronExp.append(" * ");
//周
cronExp.append("?");
}
}
else {
System.out.println("时或分或秒参数未配置" );//时或分或秒参数未配置
}
return cronExp.toString();
}
/**
*
*方法摘要:生成计划的详细描述
*@param taskScheduleVo
*@return String
*/
public static String createDescription(TaskScheduleVo taskScheduleVo){
StringBuffer description = new StringBuffer("");
//计划执行开始时间
// Date startTime = taskScheduleVo.getScheduleStartTime();
if (null != taskScheduleVo.getSecond()
&& null != taskScheduleVo.getMinute()
&& null != taskScheduleVo.getHour()) {
//按每天
if(taskScheduleVo.getJobType().intValue() == 1){
description.append("每天");
description.append(taskScheduleVo.getHour()).append("时");
description.append(taskScheduleVo.getMinute()).append("分");
description.append(taskScheduleVo.getSecond()).append("秒");
description.append("执行");
}
//按每周
else if(taskScheduleVo.getJobType().intValue() == 3){
if(taskScheduleVo.getDayOfWeeks() != null && taskScheduleVo.getDayOfWeeks().length > 0) {
String days = "";
for(int i : taskScheduleVo.getDayOfWeeks()) {
days += "周" + i;
}
description.append("每周的").append(days).append(" ");
}
if (null != taskScheduleVo.getSecond()
&& null != taskScheduleVo.getMinute()
&& null != taskScheduleVo.getHour()) {
description.append(",");
description.append(taskScheduleVo.getHour()).append("时");
description.append(taskScheduleVo.getMinute()).append("分");
description.append(taskScheduleVo.getSecond()).append("秒");
}
description.append("执行");
}
//按每月
else if(taskScheduleVo.getJobType().intValue() == 2){
//选择月份
if(taskScheduleVo.getDayOfMonths() != null && taskScheduleVo.getDayOfMonths().length > 0) {
String days = "";
for(int i : taskScheduleVo.getDayOfMonths()) {
days += i + "号";
}
description.append("每月的").append(days).append(" ");
}
description.append(taskScheduleVo.getHour()).append("时");
description.append(taskScheduleVo.getMinute()).append("分");
description.append(taskScheduleVo.getSecond()).append("秒");
description.append("执行");
}
}
return description.toString();
}
//参考例子
public static void main(String[] args) {
//执行时间:每天的12时12分12秒 start
TaskScheduleVo taskScheduleVo = new TaskScheduleVo();
taskScheduleVo.setJobType(0);//按每秒
taskScheduleVo.setSecond(30);
String cronExp = createCronExpression(taskScheduleVo);
System.out.println(cronExp);
taskScheduleVo.setJobType(4);//按每分钟
taskScheduleVo.setMinute(8);
String cronExpp = createCronExpression(taskScheduleVo);
System.out.println(cronExpp);
taskScheduleVo.setJobType(1);//按每天
Integer hour = 12; //时
Integer minute = 12; //分
Integer second = 12; //秒
taskScheduleVo.setHour(hour);
taskScheduleVo.setMinute(minute);
taskScheduleVo.setSecond(second);
String cropExp = createCronExpression(taskScheduleVo);
System.out.println(cropExp + ":" + createDescription(taskScheduleVo));
//执行时间:每天的12时12分12秒 end
taskScheduleVo.setJobType(3);//每周的哪几天执行
Integer[] dayOfWeeks = new Integer[3];
dayOfWeeks[0] = 1;
dayOfWeeks[1] = 2;
dayOfWeeks[2] = 3;
taskScheduleVo.setDayOfWeeks(dayOfWeeks);
cropExp = createCronExpression(taskScheduleVo);
System.out.println(cropExp + ":" + createDescription(taskScheduleVo));
taskScheduleVo.setJobType(2);//每月的哪几天执行
Integer[] dayOfMonths = new Integer[3];
dayOfMonths[0] = 1;
dayOfMonths[1] = 21;
dayOfMonths[2] = 13;
taskScheduleVo.setDayOfMonths(dayOfMonths);
cropExp = createCronExpression(taskScheduleVo);
System.out.println(cropExp + ":" + createDescription(taskScheduleVo));
}
}
8、Cron时间实体
package com.joe.oauth.quartz.domin;
import lombok.Data;
@Data
public class TaskScheduleVo {
/**
* 所选作业类型:
* 0 -> 每秒
* 4 -> 每分
* 1 -> 每天
* 3 -> 每周
* 2 -> 每月
*/
Integer jobType;
/**月*/
Integer[] dayOfMonths;
/**天*/
Integer[] dayOfWeeks;
/**秒 */
Integer second;
/**分 */
Integer minute;
/**时 */
Integer hour;
}
9、执行结果如下:
这块我设置的间隔10秒执行异常任务。
推荐阅读
-
任务调度框架Quartz(九) Spring Boot集成持久化Quartz定时任务管理
-
springboot集成Quartz任务调度
-
springboot——任务调度框架quartz
-
Quartz 任务调度框架
-
Spring集成Quartz定时任务框架介绍和Cron表达式详解 博客分类: spring
-
Spring集成Quartz定时任务框架介绍和Cron表达式详解 博客分类: spring
-
Spring整合Quartz实现定时任务调度的方法
-
Spring整合Quartz实现定时任务调度的方法
-
Java中Spring使用Quartz任务调度定时器
-
Spring 中使用Quartz实现任务调度