文章目录

  • ​​一、添加依赖​​
  • ​​二、添加一个简单的任务​​
  • ​​三、数据源设置​​
  • ​​四、增加一个任务​​
  • ​​五、定时任务的CRUD​​
  • ​​5.1. 启动任务​​
  • ​​5.2. 更新任务​​
  • ​​5.3. 暂停任务​​
  • ​​5.4. 恢复任务​​
  • ​​5.5. 删除任务​​
  • ​​六、持久化​​
  • ​​6.1. 系统配置文件​​
  • ​​6.2. 持久化的表​​
  • ​​6.3. 定时任务CRUD​​
  • ​​6.3.1. 获取所有计划中的任务列表​​
  • ​​6.3.2. 获取所有正在运行的任务​​
  • ​​6.3.3. 立即执行一个Job​​
  • ​​6.3.4. 恢复任务​​
  • ​​6.3.5. 暂停任务​​
  • ​​6.3.6. 删除任务​​
  • ​​6.3.7. 修改任务​​
  • ​​6.3.8. 增加任务​​

一、添加依赖

​springboot​​​官方添加了​​Quartz​​​的依赖,我们需要在​​pom​​文件中引入:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

二、添加一个简单的任务

官方的以来直接只需要继承​​QuartzBean​​:

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;

public class MyTask extends QuartzJobBean {

@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
System.out.println("简单的定时任务执行时间:" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
}

}

添加配置类将任务添加到调度任务里面:

import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class QuartzConfig {

@Bean
public JobDetail uploadTaskDetail() {
return JobBuilder.newJob(MyTask.class)
.withIdentity("MyTask")
.storeDurably()
.build();
}

// 设置触发器,并添加调度器
@Bean
public Trigger uploadTaskTrigger() {
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("*/5 * * * * ?");
return TriggerBuilder.newTrigger()
.forJob(uploadTaskDetail())
.withIdentity("MyTask")
.withSchedule(scheduleBuilder)
.build();
}
}

启动项目​​Quartz​​就可以执行:

Java定时任务之Springboot整合Quartz(三)_springboot整合

这个的任务是写死的,效果太差,下面我们看一个动态任务如何使用!

三、数据源设置

添加​​JdbcTempate​​​和​​mysql​​相关依赖:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>

添加一个任务相关的表

CREATE TABLE `quartz` (
`job_id` int(11) NOT NULL AUTO_INCREMENT,
`job_name` varchar(50) DEFAULT NULL,
`job_class` varchar(255) DEFAULT NULL,
`job_status` int(11) DEFAULT NULL,
`cron_expression` varchar(255) DEFAULT NULL,
`can_view` bit(1) DEFAULT b'1',
PRIMARY KEY (`job_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8

增加对应的实体类

@Data
public class QuartzEntity implements Serializable {
/** 任务ID */
private String jobId;
/** 任务名称 */
private String jobName;
/** 任务运行类 */
private String jobClass;
/** 任务状态 */
private Integer jobStatus;
/** 任务运行时间表达式 */
private String cronExpression;
/** 是否删除 */
private Boolean canView;
}

系统配置

spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
url: jdbc:mysql://192.168.31.26:3306/test_db?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false
logging:
level:
# 显示jdbctemplate的sql语句
org.springframework.jdbc.core.JdbcTemplate: DEBUG

四、增加一个任务

import lombok.extern.slf4j.Slf4j;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@Slf4j
public class MyTask extends QuartzJobBean {

@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
log.info("线程[{}] - [{}]执行的开始任务:", Thread.currentThread().getName(), LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
log.info("执行业务中......");
try {
Thread.sleep(10 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("线程[{}] - [{}]执行的结束任务:", Thread.currentThread().getName(), LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
}
}

五、定时任务的CRUD

先将​​JdbcTemplate​​​和​​Scheduler​​注入

@Autowired
private JdbcTemplate jdbcTemplate;

@Autowired
private Scheduler scheduler;

接着添加一个公共方法:

/** 获取单个任务 */
private QuartzEntity getOne(Integer id) throws Exception {
QuartzEntity quartzEntity = jdbcTemplate.queryForObject("select * from quartz where can_view = true and job_id = ?", new BeanPropertyRowMapper<>(QuartzEntity.class), id);
if (Objects.isNull(quartzEntity)) {
log.error("查询的数据不存在");
throw new Exception("查询的数据不存在");
}
return quartzEntity;
}

5.1. 启动任务

启动单个任务:

public String start(Integer id) throws Exception {
// 获取单个任务
QuartzEntity quartzEntity = getOne(id);
try {
// 反射获取具体的任务
Class<? extends Job> jobClass = (Class<? extends Job>) Class.forName(quartzEntity.getJobClass());
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(quartzEntity.getJobName()).build();
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartzEntity.getCronExpression());
// 设置触发器
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(quartzEntity.getJobName()).withSchedule(scheduleBuilder).build();
// 将具体任务和触发器加入到调度器
scheduler.scheduleJob(jobDetail, trigger);
} catch (ClassNotFoundException e) {
log.warn("定时任务路径");
} catch (SchedulerException e) {
log.warn("创建定时任务出错:{}", e.getMessage());
}
return "success";
}

5.2. 更新任务

/**
* 更新任务
* @param quartzEntity
* @return
*/
public String update(QuartzEntity quartzEntity) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(quartzEntity.getJobName());
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartzEntity.getCronExpression());
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
scheduler.rescheduleJob(triggerKey, trigger);
} catch (SchedulerException e) {
log.error("更新定时任务出错:{}", e.getMessage());
}
return "更新定时任务成功";
}

5.3. 暂停任务

/**
* 暂停任务
* @param id
* @return
*/
public String stop(Integer id) throws Exception {
// 获取某一个任务实体
QuartzEntity quartzEntity = getOne(id);
// 通过任务名获取任务
JobKey jobKey = JobKey.jobKey(quartzEntity.getJobName());
try {
// 暂停任务
scheduler.pauseJob(jobKey);
} catch (SchedulerException e) {
log.error("暂定定时任务出错:{}", e.getMessage());
}
return "任务暂停";
}

5.4. 恢复任务

/**
* 暂停任务
* @param id
* @return
* @throws Exception
*/
public String resume(Integer id) throws Exception {
QuartzEntity quartzEntity = getOne(id);
JobKey jobKey = JobKey.jobKey(quartzEntity.getJobName());
try {
scheduler.resumeJob(jobKey);
} catch (SchedulerException e) {
log.error("暂定定时任务出错:{}", e.getMessage());
}
return "恢复任务成功";
}

5.5. 删除任务

/**
* 删除任务
* @param id
* @return
* @throws Exception
*/
public String delete(Integer id) throws Exception {
QuartzEntity quartzEntity = getOne(id);
jdbcTemplate.update("update quartz set can_view = false where job_id = ?", quartzEntity.getJobId());
JobKey jobKey = JobKey.jobKey(quartzEntity.getJobName());
try {
scheduler.deleteJob(jobKey);
} catch (SchedulerException e) {
log.error("删除定时任务出错:{}", e.getMessage());
}
return "恢复任务成功";
}

六、持久化

6.1. 系统配置文件

我们上面是使用的是内存的持久化,如果上次没有运行完,下次重启任务就丢失了!所以下面看一下如何持久化到数据库,需要在数据库上就行操作(这样我们就可以不需要我们自定义存储任务的表了):

spring:
quartz:
# 数据库方式
job-store-type: jdbc
jdbc:
# 第一次运行时候会初始化数据表
initialize-schema: always
properties:
org:
quartz:
scheduler:
# 调度器的实例名称
instanceName: clusteresScheduler
# 设置调度器的实例ID (instanceId)如果使用集群,instanceId必须唯一,设置成AUTO
instanceId: AUTO
jobStore:
# 事务配置 先执行业务逻辑在启动任务,保证业务出错之后可以回滚
class: org.quartz.impl.jdbcjobstore.JobStoreTX
# 驱动器方言 数据库平台
driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
# 表前缀
tablePrefix: QRTZ_
# 集群模式
isClustered: auto
# 连接池配置
threadPool:
# 连接池类
class: org.quartz.simpl.SimpleThreadPool
# 总线程
threadCount: 10
# 核心线程
threadPriority: 5
# 线程继承初始化线程的上下文类加载器
threadsInheritContextClassLoaderOfInitializingThread: true

上面使用的是我们项目的数据源,我们也可以自定义​​quartz​​​数据源(很多时候需要将​​quartz​​数据源和业务数据源分类的):

spring:
quartz:
# ......
properties:
org:
quartz:
jobStore:
# ......
# Quartz自定义数据源配置
dataSource: quartzDataSource
# 自定义的数据源配置
dataSource:
quartzDataSource:
driver: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.31.26:3306/test_db?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false
user: root
password: 123456

项目启动之后生成的数据表(在​​org.quartz-scheduler:quartz:2.3.2​​​的​​org.quartz.impl.jdbcjobstore​​​下面我们可以看到初始化的​​sql​​。):

6.2. 持久化的表

表明

作用

QRTZ_FIRED_TRIGGERS

激发的触发器

QRTZ_PAUSED_TRIGGER_GRPS

暂停的触发器

QRTZ_SCHEDULER_STATE

存储集群中note实例信息,quartz会定时读取该表的信息判断集群中每个实例的当前状态

QRTZ_CRON_TRIGGERS

存储 CronTrigger,包括 Cron表达式和时区信息

QRTZ_LOCKS

存储程序的悲观锁的信息(假如使用了悲观锁)

QRTZ_JOB_DETAILS

存储每一个已配置的 Job 的详细信息

QRTZ_TRIGGERS

存储已配置的 Trigger 的信息

QRTZ_SIMPROP_TRIGGERS

QRTZ_SIMPLE_TRIGGERS

存储简单的Trigger,包括重复次数,间隔,以及已触的次数

QRTZ_BLOB_TRIGGERS

作为 Blob 类型存储(用于 Quartz 用户用 JDBC创建他们自己定制的 Trigger 类型,JobStore 并不知道如何存储实例的时候)

QRTZ_CALENDARS

存放日历信息, quartz可配置一个日历来指定一个时间范围

我们使用的是​​CronTrigger​​​触发器,我们用到的表只有:​​QRTZ_CRON_TRIGGERS​​​, ​​QRTZ_FIRED_TRIGGERS​​​, ​​QRTZ_JOB_DETAILS​​​, ​​QRTZ_TRIGGERS​​​, ​​QRTZ_LOCKS​​。

6.3. 定时任务CRUD

6.3.1. 获取所有计划中的任务列表
/**
* 获取所有计划中的任务列表
* @return
*/
public List<Map<String, Object>> list() throws Exception {
List<Map<String, Object>> jobList = new ArrayList<>();
try {
GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
for (JobKey jobKey : jobKeys) {
List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
for (Trigger trigger : triggers) {
Map<String, Object> temp = new HashMap<>();
temp.put("jobName", jobKey.getName());
temp.put("jobGroupName", jobKey.getGroup());
temp.put("description", "触发器:" + trigger.getKey());
Trigger.TriggerState state = scheduler.getTriggerState(trigger.getKey());
temp.put("status", state.name());
if (trigger instanceof CronTrigger) {
CronTrigger cronTrigger = (CronTrigger) trigger;
String cronExpression = cronTrigger.getCronExpression();
temp.put("jobName", cronExpression);
}
jobList.add(temp);
}
}
} catch (SchedulerException e) {
throw new Exception("获取任务列表失败");
}
return jobList;
}
6.3.2. 获取所有正在运行的任务
/**
* 获取正在运行的List
* @return
*/
public List<Map<String, Object>> runList() throws Exception {
List<Map<String, Object>> jobList = new ArrayList<>();
try {
List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
for (JobExecutionContext jobExecutionContext : executingJobs) {
Map<String, Object> temp = new HashMap<>();
JobDetail jobDetail = jobExecutionContext.getJobDetail();
JobKey jobKey = jobDetail.getKey();
Trigger trigger = jobExecutionContext.getTrigger();
temp.put("jobName", jobKey.getName());
temp.put("groupName", jobKey.getGroup());
temp.put("description", "触发器" + trigger.getKey());
Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
temp.put("status", triggerState.name());
if (trigger instanceof CronTrigger) {
CronTrigger cronTrigger = (CronTrigger) trigger;
String cronExpression = cronTrigger.getCronExpression();
temp.put("jobName", cronExpression);
}
jobList.add(temp);
}
} catch (SchedulerException e) {
throw new Exception("获取任务列表失败");
}
return jobList;
}
6.3.3. 立即执行一个Job
/**
* 立即执行job
* @param jobName
* @param groupName
* @return
*/
public void onceRunJob(String jobName, String groupName) throws Exception {
try {
JobKey jobKey = JobKey.jobKey(jobName, groupName);
scheduler.triggerJob(jobKey);
} catch (SchedulerException e) {
throw new Exception("立即执行Job失败");
}
}
6.3.4. 恢复任务
/**
* 恢复执行Job
* @param jobName
* @param groupName
* @throws Exception
*/
public void resumeJob(String jobName, String groupName) throws Exception {
try {
JobKey jobKey = JobKey.jobKey(jobName, groupName);
scheduler.resumeJob(jobKey);
} catch (SchedulerException e) {
throw new Exception("恢复执行Job失败");
}
}
6.3.5. 暂停任务
/**
* 暂停执行Job
* @param jobName
* @param groupName
* @throws Exception
*/
public void pauseJob(String jobName, String groupName) throws Exception {
try {
JobKey jobKey = JobKey.jobKey(jobName, groupName);
scheduler.pauseJob(jobKey);
} catch (SchedulerException e) {
throw new Exception("暂停执行Job失败");
}
}
6.3.6. 删除任务
/**
* 删除Job
* @param jobName
* @param groupName
* @throws Exception
*/
public void deleteJob(String jobName, String groupName) throws Exception {
try {
JobKey jobKey = JobKey.jobKey(jobName, groupName);
scheduler.deleteJob(jobKey);
} catch (SchedulerException e) {
throw new Exception("暂停执行Job失败");
}
}
6.3.7. 修改任务
/**
* 更新执行Job
* @param jobName
* @param groupName
* @param jobTime
* @throws Exception
*/
public void updateJob(String jobName, String groupName, String jobTime) throws Exception {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(jobName, groupName);
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
.withSchedule(CronScheduleBuilder.cronSchedule(jobTime)).build();
// 重启触发器
scheduler.rescheduleJob(triggerKey, trigger);
} catch (SchedulerException e) {
throw new Exception("更新执行Job失败");
}
}
6.3.8. 增加任务
/**
* 增加一个job
* @param cronJobParam
*/
public void addJob(CronJobParam cronJobParam) {
try {
// 创建jobDetail实例,绑定Job实现类
// 指明job的名称,所在组的名称,以及绑定job类
// 任务名称和组构成任务key
Class<? extends Job> jobClass = (Class<? extends Job>) Class.forName(cronJobParam.getJobClass());
JobDetail jobDetail = JobBuilder.newJob(jobClass)
.withIdentity(cronJobParam.getJobName(), cronJobParam.getGroupName())
.build();
// 设置job参数
if(cronJobParam.getJobData() != null && cronJobParam.getJobData().size() > 0){
jobDetail.getJobDataMap().putAll(cronJobParam.getJobData());
}
// 定义调度触发规则
// 使用cornTrigger规则
// 触发器key
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity(cronJobParam.getJobName(), cronJobParam.getGroupName())
.startAt(DateBuilder.futureDate(1, DateBuilder.IntervalUnit.SECOND))
.withSchedule(CronScheduleBuilder.cronSchedule(cronJobParam.getJobTime()))
.startNow()
.build();
// 把作业和触发器注册到任务调度中
scheduler.scheduleJob(jobDetail, trigger);
} catch (Exception e) {
e.printStackTrace();
}

}