springboot基于quartz实现的定时任务

用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

发布了65 篇原创文章 · 获赞 38 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/HelloWorldYangSong/article/details/99217307