用quartz实现定时任务的好处是:
1、quartz做了大量的封装;
2、便于任务的管理;
3、可以基于其api对任务随时启动和关闭、或修改执行任务的时间。
一、设计思路
通过JobInit类的run()方法初始化任务,如下图
任务的实现,通过实现job接口,如下图
在实现方法execute中写业务逻辑
二、数据库设计
1、新建数据库:
库名:boot-task
2、表设计:
因为本项目是基于JPA实现的,所以项目首次启动表结构就会自动建好,在控制太中会输出如下日志:
sql如下:
表:t_taks
CREATE TABLE `t_task` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`created_at` datetime DEFAULT NULL,
`created_by` varchar(64) COLLATE utf8_bin DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
`updated_by` varchar(64) COLLATE utf8_bin DEFAULT NULL,
`job_name` varchar(500) COLLATE utf8_bin NOT NULL,
`job_description` varchar(500) COLLATE utf8_bin DEFAULT NULL,
`job_class_name` varchar(500) COLLATE utf8_bin NOT NULL,
`job_id` varchar(500) COLLATE utf8_bin NOT NULL,
`job_group` varchar(500) COLLATE utf8_bin NOT NULL,
`status` int(11) DEFAULT NULL,
`cron_expression` varchar(32) COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
表:t_task_group
CREATE TABLE `t_task_group` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`created_at` datetime DEFAULT NULL,
`created_by` varchar(64) COLLATE utf8_bin DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
`updated_by` varchar(64) COLLATE utf8_bin DEFAULT NULL,
`job_group` varchar(500) COLLATE utf8_bin NOT NULL,
`job_group_name` varchar(500) COLLATE utf8_bin NOT NULL,
`status` int(11) DEFAULT NULL,
`type` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
三、核心代码说明
1、maven依赖,quartz
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
2、初始化代码:
package com.yarm.task.service.job;
import com.alibaba.fastjson.JSON;
import com.yarm.task.dao.TaskDao;
import com.yarm.task.dao.TaskGroupDao;
import com.yarm.task.pojo.dao.TaskGroupDO;
import com.yarm.task.service.TaskGroupService;
import com.yarm.task.service.TaskService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Service;
/**
* Created with IDEA
* author:Yarm.Yang
* Date:2019/8/7
* Time:9:53
* Des:项目启动初始化任务
*/
@Service
public class JobInit implements ApplicationRunner {
private static Logger log = LoggerFactory.getLogger(JobInit.class);
// 默认任务组时间
private static final String DEFAULT_TASK_GROUP_TIME = "2019-01-01 00:00:00";
@Autowired
private TaskGroupDao taskGroupDao;
@Autowired
private TaskGroupService taskGroupService;
@Autowired
private TaskService taskService;
@Override
public void run(ApplicationArguments args) throws Exception {
// 初始化默认组
this.insertDefaultGroup();
// 唤醒任务
taskService.resumeJobs();
}
/**
* 写入默认分组
*/
private void insertDefaultGroup(){
TaskGroupDO taskGroupDO = new TaskGroupDO();
taskGroupDO.setJobGroup("default");
taskGroupDO.setJobGroupName("默认组");
taskGroupDO.setType(1);
taskGroupDO.setStatus(1);
boolean exist = taskGroupDao.existsByJobGroupAndType(taskGroupDO.getJobGroup(), taskGroupDO.getType());
if(!exist){
TaskGroupDO insert = taskGroupService.insert(taskGroupDO);
log.info("初始化写入一条默认组数据:" + JSON.toJSON(insert));
}
}
}
3、业务代码实现
注意是一个类实现一个任务
package com.yarm.task.service.job;
import com.alibaba.fastjson.JSON;
import com.yarm.task.pojo.dao.TaskDO;
import com.yarm.task.service.TaskService;
import com.yarm.task.common.utils.QuartzUtils;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* Created with IDEA
* author:Yarm.Yang
* Date:2019/8/6
* Time:17:50
* Des:
*/
@Service
public class JobTest implements Job {
private static Logger log = LoggerFactory.getLogger(JobTest.class);
@Autowired
private TaskService taskService;
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
log.info("goood job");
// boolean isOk = taskService.createScheduleJob(taskDO);
// if(isOk)
// log.info("任务开始执行:" + JSON.toJSON(taskDO));
}
}
4、任务的实体类说明
package com.yarm.task.pojo.dao;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
/**
* Created with IDEA
* author:Yarm.Yang
* Date:2019/8/5
* Time:19:55
* Des:映射任务表
*/
@Entity
@Table(name = "t_task")
@EntityListeners(AuditingEntityListener.class)
public class TaskDO extends BaseDO {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)//支持mysql主键自增长
private int id;
// 任务名称
@Column(length = 500)
@NotNull(message = "任务名称不能为空")
private String jobName;
// 任务描述
@Column(length = 500)
private String jobDescription;
@Column(length = 500)
@NotNull(message = "任务执行类不能为空")
private String jobClassName;//执行类
// 任务id
@Column(length = 500, nullable = false)
private String jobId;
// 任务分组
@Column(length = 500, nullable = false)
private String jobGroup;
// 任务状态 启动还是暂停,0:启动,1:非启动
@Column(length = 4)
private int status;
// 任务运行时间表达式
@Column(length = 32, nullable = false)
@NotNull(message = "任务cron表达式不能为空")
private String cronExpression;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getJobName() {
return jobName;
}
public void setJobName(String jobName) {
this.jobName = jobName;
}
public String getJobId() {
return jobId;
}
public void setJobId(String jobId) {
this.jobId = jobId;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getCronExpression() {
return cronExpression;
}
public void setCronExpression(String cronExpression) {
this.cronExpression = cronExpression;
}
public String getJobClassName() {
return jobClassName;
}
public void setJobClassName(String jobClassName) {
this.jobClassName = jobClassName;
}
public String getJobDescription() {
return jobDescription;
}
public void setJobDescription(String jobDescription) {
this.jobDescription = jobDescription;
}
public String getJobGroup() {
return jobGroup;
}
public void setJobGroup(String jobGroup) {
this.jobGroup = jobGroup;
}
@Override
public String toString() {
return "TaskDO{" +
"id=" + id +
", jobName='" + jobName + '\'' +
", jobDescription='" + jobDescription + '\'' +
", jobClassName='" + jobClassName + '\'' +
", jobId='" + jobId + '\'' +
", jobGroup='" + jobGroup + '\'' +
", status=" + status +
", cronExpression='" + cronExpression + '\'' +
'}';
}
}
这类是比较重要的,看明白了这些成员字段的意义大致就清楚下边quartz的API了
jobId // 是每个任务的唯一标记 jobGroup // 每个任务要指定一个组,不是必须的,如果不指定将分在默认组
5、quartz的 API再次封装为工具类:
package com.yarm.task.common.utils;
import com.alibaba.fastjson.JSON;
import com.yarm.task.pojo.dao.TaskDO;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.UUID;
/**
* Created with IDEA
* author:Yarm.Yang
* Date:2019/8/5
* Time:18:15
* Des:
*/
public class QuartzUtils {
private static Logger log = LoggerFactory.getLogger(QuartzUtils.class);
/**
* 创建定时任务 定时任务创建之后默认启动状态
* @param scheduler 调度器
* @param taskDO 定时任务信息类
* @throws Exception
*/
public static boolean createScheduleJob(Scheduler scheduler, TaskDO taskDO){
try {
// 触发时间点
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(taskDO.getCronExpression().trim());
// 构建job信息
Class cls = Class.forName(taskDO.getJobClassName());
cls.newInstance();
JobDetail jobDetail = JobBuilder.newJob(cls)
.withIdentity(taskDO.getJobId(), taskDO.getJobGroup())
.withDescription(taskDO.getJobDescription())
.build();
// 构建触发器trigger
// 直接启动
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity(taskDO.getJobId())
.withSchedule(scheduleBuilder)
.startNow()
.build();
// 交由Scheduler安排触发
scheduler.scheduleJob(jobDetail,trigger);
return true;
} catch (Exception e) {
log.warn("创建定时任务失败:" + JSON.toJSON(e.getMessage()).toString());
}
return false;
}
/**
* 根据任务ID暂停定时任务
* @param scheduler 调度器
* @param jobId 定时任务ID
* @throws SchedulerException
*/
public static boolean pauseScheduleJob(Scheduler scheduler, String jobId){
JobKey jobKey = JobKey.jobKey(jobId);
try {
scheduler.pauseJob(jobKey);
return true;
} catch (SchedulerException e) {
log.warn("暂停定时任务出错:" + JSON.toJSON(e.getMessage()).toString());
}
return false;
}
/**
* 根据任务ID恢复定时任务
* @param scheduler 调度器
* @param id 定时任务ID
* @throws SchedulerException
*/
public static boolean resumeScheduleJob(Scheduler scheduler, String id) {
JobKey jobKey = JobKey.jobKey(id);
try {
scheduler.resumeJob(jobKey);
return true;
} catch (SchedulerException e) {
log.warn("启动定时任务出错:" + JSON.toJSON(e.getMessage()).toString());
}
return false;
}
/**
* 根据任务ID立即运行一次定时任务
* @param scheduler 调度器
* @param jobId 定时任务ID
* @param jobGroup 定时任务组
* @throws SchedulerException
*/
public static boolean runOnce(Scheduler scheduler, String jobId, String jobGroup){
JobKey jobKey = JobKey.jobKey(jobId,jobGroup);
try {
scheduler.triggerJob(jobKey);
return true;
} catch (SchedulerException e) {
log.warn("运行定时任务出错:" + JSON.toJSON(e.getMessage()).toString());
}
return false;
}
/**
* 更新定时任务
* @param scheduler 调度器
* @param taskDO 定时任务信息类
* @throws SchedulerException
*/
public static boolean updateScheduleJob(Scheduler scheduler, TaskDO taskDO) {
try {
// 获取到对应任务的触发器
TriggerKey triggerKey = TriggerKey.triggerKey(taskDO.getJobId());
// 设置定时任务执行方式
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(taskDO.getCronExpression());
// 重新构建任务的触发器trigger
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
trigger = trigger.getTriggerBuilder()
.withIdentity(triggerKey)
.withSchedule(scheduleBuilder)
.build();
// 重置对应的job
scheduler.rescheduleJob(triggerKey, trigger);
return true;
} catch (SchedulerException e) {
log.warn("更新定时任务出错:" + JSON.toJSON(e.getMessage()).toString());
}
return false;
}
/**
* 根据定时任务名称从调度器当中删除定时任务
* @param scheduler 调度器
* @param jobId 定时任务ID
* @throws SchedulerException
*/
public static boolean deleteScheduleJob(Scheduler scheduler, String jobId) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(jobId);
JobKey jobKey = JobKey.jobKey(jobId);
// 停止触发器
scheduler.pauseTrigger(triggerKey);
// 移除触发器
scheduler.unscheduleJob(triggerKey);
// 删除任务
scheduler.deleteJob(jobKey);
return true;
} catch (SchedulerException e) {
log.warn("删除定时任务出错:" + JSON.toJSON(e.getMessage()).toString());
}
return false;
}
/**
* 获取UUID生成的jobId
* @return
*/
public static String getJobId(){
UUID uuid=UUID.randomUUID();
String str = uuid.toString();
String uuidStr = str.replace("-", "");
return uuidStr;
}
}
这个工具类实现了创建任务、启动任务,关闭任务的基本功能
四、本项目的几个核心接口:
五、项目地址
https://github.com/15902124763/SpringBoot-Task-Parent
如果有帮到您,还请老铁给个start