一、背景
項(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 )
