springboot+quartz配置實(shí)現(xiàn)動(dòng)態(tài)定時(shí)任務(wù)

一、背景

項(xiàng)目中需要用到定時(shí)任務(wù)模塊,記錄springboot使用quartz實(shí)現(xiàn)動(dòng)態(tài)定時(shí)任務(wù),根據(jù)數(shù)據(jù)庫(kù)的數(shù)據(jù)來(lái)自定義定時(shí)任務(wù),主要記錄配置代碼實(shí)現(xiàn),本機(jī)測(cè)試是可以實(shí)現(xiàn)動(dòng)態(tài)定時(shí)任務(wù)的。

二、QuartzTask類-定時(shí)任務(wù)對(duì)象

package com.unnet.yjs.entity;

import com.baomidou.mybatisplus.activerecord.Model;
import com.baomidou.mybatisplus.annotations.TableField;
import com.baomidou.mybatisplus.annotations.TableName;
import com.baomidou.mybatisplus.enums.FieldFill;
import com.baomidou.mybatisplus.enums.FieldStrategy;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

import java.io.Serializable;
import java.util.Date;

/**
 * Email: love1208tt@foxmail.com
 * Copyright (c)  2019. missbe
 * @author lyg   19-7-11 下午10:30
 *
 *
 **/

@Setter
@Getter
@ToString
@TableName("quartz_task")
@ApiModel
public class QuartzTask extends Model<QuartzTaskLog> implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 實(shí)體編號(hào)(唯一標(biāo)識(shí))
     */

    protected Long id;
    /**
     * 任務(wù)調(diào)度參數(shù)key
     */
    @TableField(exist = false)
    public static final String JOB_PARAM_KEY = "JOB_PARAM_KEY";
    /**
     * 任務(wù)名稱
     */
    @ApiModelProperty(example = "Quartz-Hello")
    private String name;
    /**
     * 任務(wù)表達(dá)式
     */
    @ApiModelProperty(example = "3 * * * * ? ")
    private String cron;
    /**
     * 執(zhí)行的類
     */
    @TableField("target_bean")
    @ApiModelProperty(example = "helloTask")
    private String targetBean;
    /**
     * 執(zhí)行方法
     */
    @TableField("trget_method")
    @ApiModelProperty(example = "executeMethod")
    private String trgetMethod;
    /**
     * 執(zhí)行參數(shù)
     */
    @ApiModelProperty(example = "Hello-Word!")
    private String params;
    /**
     * 任務(wù)類型-
     */
    @TableField("quartz_type")
    @ApiModelProperty(example = "API_GROUPS")
    private String quartzType;
    /**
     * 任務(wù)狀態(tài) 0:正常  1:暫停
     */
    @ApiModelProperty(example = "0")
    private Integer status;

    /**
     *  創(chuàng)建者
     */
    @TableField(value = "create_by", fill = FieldFill.INSERT)
    @ApiModelProperty(hidden = true)
    protected Long createId;

    /**
     * 創(chuàng)建日期
     */
    @TableField(value = "create_date", fill = FieldFill.INSERT)
    @ApiModelProperty(hidden = true)
    protected Date createDate;

    /**
     * 更新者
     */
    @TableField(value = "update_by", fill = FieldFill.INSERT_UPDATE)
    @ApiModelProperty(hidden = true)
    protected Long updateId;

    /**
     * 更新日期
     */
    @TableField(value = "update_date", fill = FieldFill.INSERT_UPDATE)
    @ApiModelProperty(hidden = true)
    protected Date updateDate;

    /**
     * 刪除標(biāo)記(Y:正常;N:刪除;A:審核;)
     */
    @TableField(value = "del_flag")
    @ApiModelProperty(example = "0",hidden = true)
    protected Boolean delFlag = false;

    /**
     * 備注
     */
    @TableField(strategy= FieldStrategy.IGNORED)
    @ApiModelProperty(hidden = true)
    protected String remarks = "remarks";

    /**
     * 主鍵值
     */
    @Override
    protected Serializable pkVal() {
        return this.id;
    }
}

四、QuartzTaskLog類-定時(shí)任務(wù)日志對(duì)象

package com.unnet.yjs.entity;

import com.baomidou.mybatisplus.activerecord.Model;
import com.baomidou.mybatisplus.annotations.TableField;
import com.baomidou.mybatisplus.annotations.TableName;
import com.baomidou.mybatisplus.enums.FieldFill;
import com.baomidou.mybatisplus.enums.FieldStrategy;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

import java.io.Serializable;
import java.util.Date;

/**
 * Email: love1208tt@foxmail.com
 * Copyright (c)  2019. missbe
 * @author lyg   19-7-11 下午10:30
 *
 *
 **/
@Setter
@Getter
@ToString
@TableName("quartz_task_log")
public class QuartzTaskLog extends Model<QuartzTaskLog> implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 實(shí)體編號(hào)(唯一標(biāo)識(shí))
     */

    protected Long id;
    /**
     * 任務(wù)ID
     */
    @TableField("job_id")
    private Long jobId;
    /**
     * 定時(shí)任務(wù)名稱
     */
    private String name;
    /**
     * 定制任務(wù)執(zhí)行類
     */
    @TableField("target_bean")
    private String targetBean;
    /**
     * 定時(shí)任務(wù)執(zhí)行方法
     */
    @TableField("trget_method")
    private String trgetMethod;
    /**
     * 執(zhí)行參數(shù)
     */
    private String params;
    /**
     * 任務(wù)狀態(tài)
     */
    private Integer status;
    /**
     * 異常消息
     */
    private String error;
    /**
     * 執(zhí)行時(shí)間
     */
    private Integer times;

    /**
     *  創(chuàng)建者
     */
    @TableField(value = "create_by", fill = FieldFill.INSERT)
    @ApiModelProperty(hidden = true)
    protected Long createId;

    /**
     * 創(chuàng)建日期
     */
    @TableField(value = "create_date", fill = FieldFill.INSERT)
    @ApiModelProperty(hidden = true)
    protected Date createDate;

    /**
     * 更新者
     */
    @TableField(value = "update_by", fill = FieldFill.INSERT_UPDATE)
    @ApiModelProperty(hidden = true)
    protected Long updateId;

    /**
     * 更新日期
     */
    @TableField(value = "update_date", fill = FieldFill.INSERT_UPDATE)
    @ApiModelProperty(hidden = true)
    protected Date updateDate;

    /**
     * 刪除標(biāo)記(Y:正常;N:刪除;A:審核;)
     */
    @TableField(value = "del_flag")
    @ApiModelProperty(example = "0",hidden = true)
    protected Boolean delFlag;

    /**
     * 備注
     */
    @TableField(strategy= FieldStrategy.IGNORED)
    @ApiModelProperty(hidden = true)
    protected String remarks;

    /**
     * 主鍵值
     */
    @Override
    protected Serializable pkVal() {
        return this.id;
    }
}

五、QuartzTaskService和QuartzTaskServiceImpl類-定時(shí)任務(wù)Service

package com.unnet.yjs.service;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.baomidou.mybatisplus.service.IService;
import com.unnet.yjs.entity.QuartzTask;

import java.util.List;

/**
 * Email: love1208tt@foxmail.com
 * Copyright (c)  2019. missbe
 * @author lyg   19-7-11 下午10:30
 * 定時(shí)任務(wù) 服務(wù)類
 *
 **/
public interface QuartzTaskService extends IService<QuartzTask> {

    /**
     * 根據(jù)ID,查詢定時(shí)任務(wù)
     */
    QuartzTask queryObject(Long jobId);

    /**
     * 分頁(yè)查詢定時(shí)任務(wù)列表
     */
    Page<QuartzTask> queryList(EntityWrapper<QuartzTask> wrapper, Page<QuartzTask> page);

    /**
     * 保存定時(shí)任務(wù)
     */
    void saveQuartzTask(QuartzTask quartzTask);

    /**
     * 更新定時(shí)任務(wù)
     */
    void updateQuartzTask(QuartzTask quartzTask);

    /**
     * 批量刪除定時(shí)任務(wù)
     */
    void deleteBatchTasks(List<Long> ids);

    /**
     * 批量更新定時(shí)任務(wù)狀態(tài)
     */
    int updateBatchTasksByStatus(List<Long> ids, Integer status);

    /**
     * 立即執(zhí)行
     */
    void run(List<Long> jobIds);

    /**
     * 暫停運(yùn)行
     */
    void paush(List<Long> jobIds);

    /**
     * 恢復(fù)運(yùn)行
     */
    void resume(List<Long> jobIds);
}
package com.unnet.yjs.service.impl;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.unnet.yjs.controller.api.RootController;
import com.unnet.yjs.dao.QuartzTaskDao;
import com.unnet.yjs.entity.QuartzTask;
import com.unnet.yjs.service.QuartzTaskService;
import com.unnet.yjs.util.Constants;
import com.unnet.yjs.util.quartz.ScheduleUtils;
import org.quartz.CronTrigger;
import org.quartz.Scheduler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.List;

/**
 * Email: love1208tt@foxmail.com
 * Copyright (c)  2019. missbe
 * @author lyg   19-7-11 下午10:31
 *
 * 定時(shí)任務(wù) 服務(wù)實(shí)現(xiàn)類
 * @author wangl
 * @since 2018-01-24
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class QuartzTaskServiceImpl extends ServiceImpl<QuartzTaskDao, QuartzTask> implements QuartzTaskService {
    private static final Logger LOGGER = LoggerFactory.getLogger(QuartzTaskService.class);

    @Resource
    private Scheduler scheduler;

    /**
     * 項(xiàng)目啟動(dòng)時(shí),初始化定時(shí)器
     */
    @PostConstruct
    public void init() {
        EntityWrapper<QuartzTask> wrapper = new EntityWrapper<>();
        wrapper.eq("del_flag", false);
        wrapper.eq("quartz_type", Constants.QUARTZ_API_GROUPS);
        List<QuartzTask> scheduleJobList = selectList(wrapper);
        for (QuartzTask scheduleJob : scheduleJobList) {
            CronTrigger cronTrigger = ScheduleUtils.getCronTrigger(scheduler, scheduleJob.getId());
            //如果不存在,則創(chuàng)建
            if (cronTrigger == null) {
                LOGGER.info("Quartz創(chuàng)建定時(shí)任務(wù)成功,任務(wù):" + scheduleJob);
                ScheduleUtils.createScheduleJob(scheduler, scheduleJob);
            } else {
                LOGGER.info("Quartz更新定時(shí)任務(wù)成功,任務(wù):" + scheduleJob);
                ScheduleUtils.updateScheduleJob(scheduler, scheduleJob);
            }
        }
    }

    @Override
    public QuartzTask queryObject(Long jobId) {
        return baseMapper.selectById(jobId);
    }

    @Override
    public Page<QuartzTask> queryList(EntityWrapper<QuartzTask> wrapper, Page<QuartzTask> page) {
        return selectPage(page, wrapper);
    }

    @Override
    public void saveQuartzTask(QuartzTask quartzTask) {
        baseMapper.insert(quartzTask);
        LOGGER.info("Quartz創(chuàng)建定時(shí)任務(wù)成功,任務(wù):" + quartzTask);
        ScheduleUtils.createScheduleJob(scheduler, quartzTask);
    }

    @Override
    public void updateQuartzTask(QuartzTask quartzTask) {
        baseMapper.updateById(quartzTask);
        LOGGER.info("Quartz更新定時(shí)任務(wù)成功,任務(wù):" + quartzTask);
        ScheduleUtils.updateScheduleJob(scheduler, quartzTask);
    }

    @Override
    public void deleteBatchTasks(List<Long> ids) {
        for (Long id : ids) {
            LOGGER.info("Quartz批量刪除定時(shí)任務(wù)成功,任務(wù)ID:" + id);
            ScheduleUtils.deleteScheduleJob(scheduler, id);
        }
        deleteBatchIds(ids);
    }

    @Override
    public int updateBatchTasksByStatus(List<Long> ids, Integer status) {
        List<QuartzTask> list = selectBatchIds(ids);
        for (QuartzTask task : list) {
            task.setStatus(status);
        }
        updateBatchById(list);
        return 0;
    }

    @Override
    public void run(List<Long> jobIds) {
        for (Long jobId : jobIds) {
            LOGGER.info("Quartz批量立即運(yùn)行定時(shí)任務(wù)成功,任務(wù)ID:" + jobId);
            ScheduleUtils.run(scheduler, queryObject(jobId));
        }
    }

    @Override
    public void paush(List<Long> jobIds) {
        for (Long jobId : jobIds) {
            LOGGER.info("Quartz批量暫停運(yùn)行定時(shí)任務(wù)成功,任務(wù)ID:" + jobId);
            ScheduleUtils.pauseJob(scheduler, jobId);
        }
        updateBatchTasksByStatus(jobIds, Constants.QUARTZ_STATUS_PUSH);
    }

    @Override
    public void resume(List<Long> jobIds) {
        for (Long jobId : jobIds) {
            LOGGER.info("Quartz批量恢復(fù)運(yùn)行定時(shí)任務(wù)成功,任務(wù)ID:" + jobId);
            ScheduleUtils.resumeJob(scheduler, jobId);
        }

        updateBatchTasksByStatus(jobIds, Constants.QUARTZ_STATUS_NOMAL);
    }
}

六、QuartzTaskLogService和QuartzTaskLogServiceImpl類-定時(shí)任務(wù)日志Service

package com.unnet.yjs.service;

import com.baomidou.mybatisplus.service.IService;
import com.unnet.yjs.entity.QuartzTaskLog;

/**
 * Email: love1208tt@foxmail.com
 * Copyright (c)  2019. missbe
 * @author lyg   19-7-11 下午10:30
 * 任務(wù)執(zhí)行日志 服務(wù)類
 *
 **/

public interface QuartzTaskLogService extends IService<QuartzTaskLog> {
}
package com.unnet.yjs.service.impl;

import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.unnet.yjs.dao.QuartzTaskLogDao;
import com.unnet.yjs.entity.QuartzTaskLog;
import com.unnet.yjs.service.QuartzTaskLogService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * Email: love1208tt@foxmail.com
 * Copyright (c)  2019. missbe
 * @author lyg   19-7-11 下午10:31
 * 任務(wù)執(zhí)行日志 服務(wù)實(shí)現(xiàn)類
 *
 **/

@Service
@Transactional(rollbackFor = Exception.class)
public class QuartzTaskLogServiceImpl extends ServiceImpl<QuartzTaskLogDao, QuartzTaskLog> implements QuartzTaskLogService {

}

七、QuartzTaskController類-定時(shí)任務(wù)Controller

package com.unnet.yjs.controller.api.v1;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.unnet.yjs.annotation.HttpMethod;
import com.unnet.yjs.entity.QuartzTask;
import com.unnet.yjs.service.QuartzTaskService;
import com.unnet.yjs.util.Constants;
import com.unnet.yjs.util.LayerData;
import com.unnet.yjs.util.RestResponse;
import io.swagger.annotations.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.util.WebUtils;

import javax.annotation.Resource;
import javax.servlet.ServletRequest;
import java.util.List;
import java.util.Map;

/**
 * Email: love1208tt@foxmail.com
 * Copyright (c)  2019. missbe
 * @author lyg   19-7-11 下午10:39
 *
 * 定時(shí)任務(wù)  前端控制器
 *
 * @author wangl
 * @since 2018-01-24
 */
@RestController
@Api(tags = "QuartzTaskController",description = "定時(shí)任務(wù)模塊API")
@RequestMapping(Constants.V1_API_PREFIX+"/admin/quartzTask")
public class QuartzTaskController {
    private static final Logger LOGGER = LoggerFactory.getLogger(QuartzTaskController.class);

    @Resource
    private QuartzTaskService quartzTaskService;

    @PostMapping("list")
    @ApiOperation(value = "定時(shí)任務(wù)列表",httpMethod = com.unnet.yjs.annotation.HttpMethod.GET)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "p_page", value = "頁(yè)數(shù)", defaultValue = "1", dataType = "Integer", paramType = "query"),
            @ApiImplicitParam(name = "p_limit", value = "每頁(yè)條數(shù)", defaultValue = "10", dataType = "Integer", paramType = "query")
    })
    public LayerData<QuartzTask> list(@RequestParam(value = "p_page",defaultValue = "1")Integer page,
                                      @RequestParam(value = "p_page",defaultValue = "10")Integer limit,
                                      ServletRequest request){
        Map map = WebUtils.getParametersStartingWith(request, "s_");
        LayerData<QuartzTask> layerData = new LayerData<>();
        EntityWrapper<QuartzTask> wrapper = new EntityWrapper<>();
        wrapper.eq("del_flag",false);
        if(!map.isEmpty()){
            String name = (String) map.get("name");
            if(StringUtils.isNotBlank(name)) {
                wrapper.like("name",name);
            }else{
                map.remove("name");
            }

            String status = (String) map.get("status");
            if(StringUtils.isNotBlank(status)) {
                wrapper.eq("status",status);
            }else{
                map.remove("status");
            }

        }
        Page<QuartzTask> pageData = quartzTaskService.queryList(wrapper,new Page<>(page,limit));
        layerData.setData(pageData.getRecords());
        layerData.setCount(pageData.getTotal());
        return layerData;
    }


    @PostMapping("add")
    @ResponseBody
    @ApiOperation(value = "新增定時(shí)任務(wù)",httpMethod = HttpMethod.POST)
    public RestResponse add(@RequestBody @ApiParam QuartzTask quartzTask){
        quartzTaskService.saveQuartzTask(quartzTask);
        return RestResponse.success();
    }


    @PostMapping("edit")
    @ApiOperation(value = "更新定時(shí)任務(wù)",httpMethod = HttpMethod.POST)
    public RestResponse edit(@RequestBody @ApiParam QuartzTask quartzTask){
        if(null == quartzTask.getId() || 0 == quartzTask.getId()){
            return RestResponse.failure("ID不能為空");
        }
        quartzTaskService.updateQuartzTask(quartzTask);
        return RestResponse.success();
    }

    @PostMapping("delete")
    @ApiOperation(value = "刪除定時(shí)任務(wù)",httpMethod = HttpMethod.POST)
    public RestResponse delete(@RequestParam(value = "ids[]",required = false)List<Long> ids){
        if(null == ids || 0 == ids.size()){
            return RestResponse.failure("ID不能為空");
        }
        quartzTaskService.deleteBatchTasks(ids);
        return RestResponse.success();
    }

    /**
     * 暫停選中的定時(shí)任務(wù)
     * @param ids 任務(wù)ID List
     */
    @PostMapping("paush")
    @ApiOperation(value = "暫停定時(shí)任務(wù)",httpMethod = HttpMethod.POST)
    public RestResponse paush(@RequestParam(value = "ids[]",required = false)List<Long> ids){
        if(null == ids || 0 == ids.size()){
            return RestResponse.failure("ID不能為空");
        }
        quartzTaskService.paush(ids);
        return RestResponse.success();
    }

    /**
     * 恢復(fù)選中的定時(shí)任務(wù)運(yùn)行
     * @param ids 任務(wù)ID List
     */
    @PostMapping("resume")
    @ApiOperation(value = "恢復(fù)定時(shí)任務(wù)",httpMethod = HttpMethod.POST)
    public RestResponse resume(@RequestParam(value = "ids[]",required = false)List<Long> ids){
        if(null == ids || 0 == ids.size()){
            return RestResponse.failure("ID不能為空");
        }
        quartzTaskService.resume(ids);
        return RestResponse.success();
    }

    /**
     * 立即執(zhí)行選中的定時(shí)任務(wù)
     * @param ids 任務(wù)ID List
     */
    @PostMapping("run")
    @ApiOperation(value = "運(yùn)行定時(shí)任務(wù)",httpMethod = HttpMethod.POST)
    public RestResponse run(@RequestParam(value = "ids[]",required = false)List<Long> ids){
        if(null == ids || 0 == ids.size()){
            return RestResponse.failure("ID不能為空");
        }
        quartzTaskService.run(ids);
        return RestResponse.success();
    }

}

八、QuartzTaskLogController類-定時(shí)任務(wù)日志Controller

package com.unnet.yjs.controller.api.v1;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.unnet.yjs.entity.QuartzTaskLog;
import com.unnet.yjs.service.QuartzTaskLogService;
import com.unnet.yjs.util.Constants;
import com.unnet.yjs.util.LayerData;
import com.unnet.yjs.util.RestResponse;
import io.swagger.annotations.Api;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.util.WebUtils;

import javax.annotation.Resource;
import javax.servlet.ServletRequest;
import java.util.Map;

/**
 * Email: love1208tt@foxmail.com
 * Copyright (c)  2019. missbe
 * @author lyg   19-7-11 下午10:39
 *
 *
 * 任務(wù)執(zhí)行日志  前端控制器
 * @author wangl
 * @since 2018-01-25
 */
@Controller
@Api(tags = "QuartzTaskLogController",description = "定時(shí)任務(wù)日志API")
@RequestMapping(Constants.V1_API_PREFIX+"/admin/quartzTaskLog")
public class QuartzTaskLogController {
    private static final Logger LOGGER = LoggerFactory.getLogger(QuartzTaskLogController.class);

    @Resource
    private QuartzTaskLogService quartzTaskLogService;

    @ResponseBody
    public LayerData<QuartzTaskLog> list(@RequestParam(value = "page",defaultValue = "1")Integer page,
                                         @RequestParam(value = "limit",defaultValue = "10")Integer limit,
                                         ServletRequest request){
        Map map = WebUtils.getParametersStartingWith(request, "s_");
        LayerData<QuartzTaskLog> layerData = new LayerData<>();
        EntityWrapper<QuartzTaskLog> wrapper = new EntityWrapper<>();
        wrapper.eq("del_flag",false);
        if(!map.isEmpty()){
            String name = (String) map.get("name");
            if(StringUtils.isNotBlank(name)) {
                wrapper.like("name",name);
            }else{
                map.remove("name");
            }

        }
        Page<QuartzTaskLog> pageData = quartzTaskLogService.selectPage(new Page<>(page,limit),wrapper);
        layerData.setData(pageData.getRecords());
        layerData.setCount(pageData.getTotal());
        return layerData;
    }

    @PostMapping("add")
    @ResponseBody
    public RestResponse add(QuartzTaskLog quartzTaskLog){
        quartzTaskLogService.insert(quartzTaskLog);
        return RestResponse.success();
    }


    @PostMapping("edit")
    @ResponseBody
    public RestResponse edit(QuartzTaskLog quartzTaskLog){
        if(null == quartzTaskLog.getId() || 0 == quartzTaskLog.getId()){
            return RestResponse.failure("ID不能為空");
        }
        quartzTaskLogService.updateById(quartzTaskLog);
        return RestResponse.success();
    }

    @PostMapping("delete")
    @ResponseBody
    public RestResponse delete(@RequestParam(value = "id",required = false)Long id){
        if(null == id || 0 == id){
            return RestResponse.failure("ID不能為空");
        }
        QuartzTaskLog quartzTaskLog = quartzTaskLogService.selectById(id);
        quartzTaskLog.setDelFlag(true);
        quartzTaskLogService.updateById(quartzTaskLog);
        return RestResponse.success();
    }

}

九、Quartz相關(guān)類

package com.unnet.yjs.util.quartz;

import com.unnet.yjs.entity.QuartzTask;
import com.unnet.yjs.entity.QuartzTaskLog;
import org.apache.commons.lang3.StringUtils;
import org.quartz.JobExecutionContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;


/**
 * Email: love1208tt@foxmail.com
 * Copyright (c)  2019. missbe
 * @author lyg   19-7-11 下午10:35
 *
 * 類ScheduleJob的功能描述:
 * 定時(shí)任務(wù)
 * @date 2017-08-25 11:48:34
 */
public class ScheduleJob extends QuartzJobBean {
    private Logger logger = LoggerFactory.getLogger(getClass());
    private final ThreadLocal<ExecutorService> service = ThreadLocal.withInitial(Executors::newSingleThreadExecutor);

    @Override
    protected void executeInternal(JobExecutionContext context) {
        QuartzTask scheduleJob = (QuartzTask) context.getMergedJobDataMap().get(QuartzTask.JOB_PARAM_KEY);
        String param = scheduleJob.getParams();
        //數(shù)據(jù)庫(kù)保存執(zhí)行記錄
        QuartzTaskLog log = new QuartzTaskLog();
        log.setJobId(scheduleJob.getId());
        log.setTargetBean(scheduleJob.getTargetBean());
        log.setTrgetMethod(scheduleJob.getTrgetMethod());
        log.setParams(param);
        log.setName("執(zhí)行定時(shí)任務(wù)【"+scheduleJob.getName()+"】");
        if(StringUtils.isNotBlank(param) && StringUtils.isNumeric(param)){
            log.setCreateId(Long.valueOf(param));
            log.setUpdateId(Long.valueOf(param));
        }else{
            //定義死
            log.setCreateId(1L);
            log.setUpdateId(1L);
        }
        log.setCreateDate(new Date());
        //任務(wù)開(kāi)始時(shí)間
        long startTime = System.currentTimeMillis();
        
        try {
            //執(zhí)行任務(wù)
            logger.info("任務(wù)準(zhǔn)備執(zhí)行,任務(wù)ID:" + scheduleJob.getId());
            ScheduleRunnable task = new ScheduleRunnable(scheduleJob.getTargetBean(),
                    scheduleJob.getTrgetMethod(), scheduleJob.getParams());
            Future<?> future = service.get().submit(task);
            
            future.get();
            
            //任務(wù)執(zhí)行總時(shí)長(zhǎng)
            long times = System.currentTimeMillis() - startTime;
            log.setTimes((int)times);
            //任務(wù)狀態(tài)    0:成功    1:失敗
            log.setStatus(0);
            
            logger.info("任務(wù)執(zhí)行完畢,任務(wù)ID:" + scheduleJob.getId() + "  總共耗時(shí):" + times + "毫秒");
        } catch (Exception e) {
            logger.error("任務(wù)執(zhí)行失敗,任務(wù)ID:" + scheduleJob.getId(), e);
            
            //任務(wù)執(zhí)行總時(shí)長(zhǎng)
            long times = System.currentTimeMillis() - startTime;
            log.setTimes((int)times);

            //任務(wù)狀態(tài)    0:成功    1:失敗
            log.setStatus(1);
            log.setError(e.getMessage());
        }finally {
            log.insert();
        }
    }
}
package com.unnet.yjs.util.quartz;

import com.unnet.yjs.exception.MyException;
import com.unnet.yjs.util.SpringUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;

/**
 * Email: love1208tt@foxmail.com
 * Copyright (c)  2019. missbe
 * @author lyg   19-7-11 下午10:35
 *
 * 類ScheduleRunnable的功能描述:
 * 執(zhí)行定時(shí)任務(wù)
 * @date 2017-08-25 16:18:02
 */
public class ScheduleRunnable implements Runnable {
    private Object target;
    private Method method;
    private String params;
    
    ScheduleRunnable(String beanName, String methodName, String params) throws NoSuchMethodException, SecurityException {

        this.target = SpringUtil.getBean(beanName);
        this.params = params;
        
        if(StringUtils.isNotBlank(params)){
            this.method = target.getClass().getDeclaredMethod(methodName, String.class);
        }else{
            this.method = target.getClass().getDeclaredMethod(methodName);
        }
    }

    @Override
    public void run() {
        try {
            ReflectionUtils.makeAccessible(method);
            if(StringUtils.isNotBlank(params)){
                method.invoke(target, params);
            }else{
                method.invoke(target);
            }
        }catch (Exception e) {
            throw new MyException("執(zhí)行定時(shí)任務(wù)失敗", e);
        }
    }

}
package com.unnet.yjs.util.quartz.task;

import com.xiaoleilu.hutool.date.DateUtil;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * Email: love1208tt@foxmail.com
 * Copyright (c)  2019. missbe
 * @author lyg   19-7-11 下午10:49
 *
 *
 **/
@Component("helloTask")
public class HelloTask {

    public void executeMethod(String params){
        System.out.println(DateUtil.format(new Date(),"yyy-MM-dd hh:mm:ss") + "->hello." + params);
    }
}
package com.unnet.yjs.util.quartz;

import com.unnet.yjs.entity.QuartzTask;
import com.unnet.yjs.exception.MyException;
import com.unnet.yjs.util.Constants;
import org.quartz.*;


/**
 * Email: love1208tt@foxmail.com
 * Copyright (c)  2019. missbe
 * @author lyg   19-7-11 下午10:35
 *
 * 類ScheduleUtils的功能描述:
 * 定時(shí)任務(wù)工具類
 * @author Administrator
 * @date 2017-08-25 16:18:10
 */
public class ScheduleUtils {
    private final static String JOB_NAME = "TASK_API_GROUPS_";
    private final static String TRIGGER_NAME = "TRIGGER_API_GROUPS_";

    /**
     * 獲取觸發(fā)器key
     */
    public static TriggerKey getTriggerKey(Long jobId) {
        return TriggerKey.triggerKey(TRIGGER_NAME + jobId);
    }
    
    /**
     * 獲取jobKey
     */
    public static JobKey getJobKey(Long jobId) {
        return JobKey.jobKey(JOB_NAME + jobId);
    }

    /**
     * 獲取表達(dá)式觸發(fā)器
     */
    public static CronTrigger getCronTrigger(Scheduler scheduler, Long jobId) {
        try {
            return (CronTrigger) scheduler.getTrigger(getTriggerKey(jobId));
        } catch (SchedulerException e) {
            throw new MyException("獲取定時(shí)任務(wù)CronTrigger出現(xiàn)異常", e);
        }
    }

    /**
     * 創(chuàng)建定時(shí)任務(wù)
     */
    public static void createScheduleJob(Scheduler scheduler, QuartzTask scheduleJob) {
        try {
            //構(gòu)建job信息
            JobDetail jobDetail = JobBuilder.newJob(ScheduleJob.class).withIdentity(getJobKey(scheduleJob.getId())).build();

            //表達(dá)式調(diào)度構(gòu)建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCron())
                    .withMisfireHandlingInstructionDoNothing();

            //按新的cronExpression表達(dá)式構(gòu)建一個(gè)新的trigger
            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(scheduleJob.getId())).withSchedule(scheduleBuilder).build();

            //放入?yún)?shù),運(yùn)行時(shí)的方法可以獲取
            jobDetail.getJobDataMap().put(QuartzTask.JOB_PARAM_KEY, scheduleJob);
            
            scheduler.scheduleJob(jobDetail, trigger);
            
            //暫停任務(wù)
            if(scheduleJob.getStatus().intValue() == Constants.QUARTZ_STATUS_PUSH.intValue()){
                pauseJob(scheduler, scheduleJob.getId());
            }
        } catch (SchedulerException e) {
            throw new MyException("創(chuàng)建定時(shí)任務(wù)失敗", e);
        }
    }
    
    /**
     * 更新定時(shí)任務(wù)
     */
    public static void updateScheduleJob(Scheduler scheduler, QuartzTask scheduleJob) {
        try {
            TriggerKey triggerKey = getTriggerKey(scheduleJob.getId());

            //表達(dá)式調(diào)度構(gòu)建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCron())
                    .withMisfireHandlingInstructionDoNothing();

            CronTrigger trigger = getCronTrigger(scheduler, scheduleJob.getId());
            
            //按新的cronExpression表達(dá)式重新構(gòu)建trigger
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
            
            //參數(shù)
            trigger.getJobDataMap().put(QuartzTask.JOB_PARAM_KEY, scheduleJob);
            
            scheduler.rescheduleJob(triggerKey, trigger);
            
            //暫停任務(wù)
            if(scheduleJob.getStatus().intValue() == Constants.QUARTZ_STATUS_PUSH.intValue()){
                pauseJob(scheduler, scheduleJob.getId());
            }
            
        } catch (SchedulerException e) {
            throw new MyException("更新定時(shí)任務(wù)失敗", e);
        }
    }

    /**
     * 立即執(zhí)行任務(wù)
     */
    public static void run(Scheduler scheduler, QuartzTask scheduleJob) {
        try {
            //參數(shù)
            JobDataMap dataMap = new JobDataMap();
            dataMap.put(QuartzTask.JOB_PARAM_KEY, scheduleJob);
            
            scheduler.triggerJob(getJobKey(scheduleJob.getId()), dataMap);
        } catch (SchedulerException e) {
            throw new MyException("立即執(zhí)行定時(shí)任務(wù)失敗", e);
        }
    }

    /**
     * 暫停任務(wù)
     */
    public static void pauseJob(Scheduler scheduler, Long jobId) {
        try {
            scheduler.pauseJob(getJobKey(jobId));
        } catch (SchedulerException e) {
            throw new MyException("暫停定時(shí)任務(wù)失敗", e);
        }
    }

    /**
     * 恢復(fù)任務(wù)
     */
    public static void resumeJob(Scheduler scheduler, Long jobId) {
        try {
            scheduler.resumeJob(getJobKey(jobId));
        } catch (SchedulerException e) {
            throw new MyException("暫停定時(shí)任務(wù)失敗", e);
        }
    }

    /**
     * 刪除定時(shí)任務(wù)
     */
    public static void deleteScheduleJob(Scheduler scheduler, Long jobId) {
        try {
            scheduler.deleteJob(getJobKey(jobId));
        } catch (SchedulerException e) {
            throw new MyException("刪除定時(shí)任務(wù)失敗", e);
        }
    }
}

ScheduleUtils封裝了Quartz的定時(shí)任務(wù)相關(guān)操作;ReflectionUtils封裝了反射的相關(guān)操作;ScheduleRunnable類實(shí)現(xiàn)了Runable接口,重寫(xiě)run方法,使用反射來(lái)調(diào)用定時(shí)任務(wù)類的方法;ScheduleJob類重寫(xiě)執(zhí)行方法,實(shí)現(xiàn)定時(shí)任務(wù)的自定義任務(wù)邏輯,定時(shí)任務(wù)會(huì)按間隔執(zhí)行這個(gè)方法;HelloTask類具體的定時(shí)任務(wù)類和具體的執(zhí)行方法;

十、Springboot配置Quartz的配置類

package com.unnet.yjs.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.Properties;


/**
 *  Email: love1208tt@foxmail.com
 *  Copyright (c) 2018. missbe
 *  @author lyg  19-5-21 下午9:43
 *
 **/

@Configuration
public class QuartzConfig {

    @Value("${spring.datasource.username}")
    private String username;

    @Value("${spring.datasource.password}")
    private String password;

    @Value("${spring.datasource.driver-class-name}")
    private String driver;

    @Value("${spring.datasource.url}")
    private String url;

    @Value("${spring.datasource.druid.initialSize}")
    private String initialSize;

    @Value("${spring.datasource.druid.maxActive}")
    private String maxActive;

    @Bean
    public DataSource dataSource() throws SQLException {
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setUsername(username);
        druidDataSource.setPassword(password);
        druidDataSource.setDriverClassName(driver);
        druidDataSource.setUrl(url);
        druidDataSource.setMaxActive(Integer.valueOf(maxActive));
        /*Druid日志記錄*/
        druidDataSource.setFilters("stat,wall,log4j");
        druidDataSource.setInitialSize(Integer.valueOf(initialSize));
        return druidDataSource;
    }

    @Bean
    public SchedulerFactoryBean schedulerFactoryBean() throws SQLException {
        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        schedulerFactoryBean.setDataSource(dataSource());
        //quartz參數(shù)
        Properties prop = new Properties();
        prop.put("org.quartz.scheduler.instanceName", "OneClickApiGroupsScheduler");
        prop.put("org.quartz.scheduler.instanceId", "AUTO");
        //線程池配置
        prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
        prop.put("org.quartz.threadPool.threadCount", "25");
        prop.put("org.quartz.threadPool.threadPriority", "5");
        //JobStore配置
        prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
        //集群配置
        prop.put("org.quartz.jobStore.isClustered", "true");
        prop.put("org.quartz.jobStore.clusterCheckinInterval", "20000");
        prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1");

        prop.put("org.quartz.jobStore.misfireThreshold", "60000");
        prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
        schedulerFactoryBean.setQuartzProperties(prop);

        schedulerFactoryBean.setSchedulerName("OneClickApiGroupsScheduler");
        //延時(shí)啟動(dòng)
        schedulerFactoryBean.setStartupDelay(20);
        schedulerFactoryBean.setApplicationContextSchedulerContextKey("applicationContextKey");
        //可選,QuartzScheduler 啟動(dòng)時(shí)更新己存在的Job,這樣就不用每次修改targetObject后刪除qrtz_job_details表對(duì)應(yīng)記錄了
        schedulerFactoryBean.setOverwriteExistingJobs(true);
        //設(shè)置自動(dòng)啟動(dòng),默認(rèn)為true | 開(kāi)啟定時(shí)任務(wù)的開(kāi)關(guān)
        schedulerFactoryBean.setAutoStartup(true);

        return schedulerFactoryBean;
    }
}

數(shù)據(jù)源可以自己配置,屬性名可以修改; schedulerFactoryBean.setAutoStartup(true);這個(gè)方法要高設(shè)為true,否則定時(shí)任務(wù)不會(huì)啟動(dòng);

十一、測(cè)試

  • 新增定時(shí)任務(wù)


    image.png
  • 定時(shí)任務(wù)執(zhí)行日志
2019-07-14 15:00:25.781  INFO 25724 --- [nio-9080-exec-6] com.unnet.yjs.service.QuartzTaskService  : Quartz創(chuàng)建定時(shí)任務(wù)成功,任務(wù):QuartzTask(id=9, name=Quartz-Hello, cron=3 * * * * ? , targetBean=helloTask, trgetMethod=executeMethod, params=Hello-Word!, quartzType=API_GROUPS, status=0, createId=1563087625757, createDate=Sun Jul 14 15:00:25 CST 2019, updateId=1563087625757, updateDate=Sun Jul 14 15:00:25 CST 2019, delFlag=false, remarks=remarks)
 Time:20 ms - ID:com.unnet.yjs.dao.QuartzTaskDao.insert
 Execute SQL:
    INSERT 
    INTO
        quartz_task
        ( `name`,  cron,  target_bean,  trget_method,  params,  quartz_type,  `status`,create_by,create_date,update_by,update_date,  del_flag,remarks )  
    VALUES
        ( 'Quartz-Hello',  '3 * * * * ? ',  'helloTask',  'executeMethod',  'Hello-Word!',  'API_GROUPS',  0,1563087625757,'2019-07-14 15:00:25.755',1563087625757,'2019-07-14 15:00:25.757',  0,'remarks' )

2019-07-14 15:01:03.030  INFO 25724 --- [eduler_Worker-1] com.unnet.yjs.util.quartz.ScheduleJob    : 任務(wù)準(zhǔn)備執(zhí)行,任務(wù)ID:9
2019-07-14 03:01:03->hello.Hello-Word!
2019-07-14 15:01:03.053  INFO 25724 --- [eduler_Worker-1] com.unnet.yjs.util.quartz.ScheduleJob    : 任務(wù)執(zhí)行完畢,任務(wù)ID:9  總共耗時(shí):23毫秒
2019-07-14 15:01:03.056  INFO 25724 --- [eduler_Worker-1] c.unnet.yjs.config.SysMetaObjectHandler  : 正在調(diào)用該insert填充字段方法
 Time:12 ms - ID:com.unnet.yjs.dao.QuartzTaskLogDao.insert
 Execute SQL:
    INSERT 
    INTO
        quartz_task_log
        ( job_id,  `name`,  target_bean,  trget_method,  params,  `status`,    times,create_by,create_date,update_by,update_date,  remarks )  
    VALUES
        ( 9,  '執(zhí)行定時(shí)任務(wù)【Quartz-Hello】',  'helloTask',  'executeMethod',  'Hello-Word!',  0,    23,1,'2019-07-14 15:01:03.03',1,'2019-07-14 15:01:03.056',  null )
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容