一、背景
项目中需要用到定时任务模块,记录springboot使用quartz实现动态定时任务,根据数据库的数据来自定义定时任务,主要记录配置代码实现,本机测试是可以实现动态定时任务的。
二、QuartzTask类-定时任务对象
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;
/**
* 实体编号(唯一标识)
*/
protected Long id;
/**
* 任务调度参数key
*/
@TableField(exist = false)
public static final String JOB_PARAM_KEY = "JOB_PARAM_KEY";
/**
* 任务名称
*/
@ApiModelProperty(example = "Quartz-Hello")
private String name;
/**
* 任务表达式
*/
@ApiModelProperty(example = "3 * * * * ? ")
private String cron;
/**
* 执行的类
*/
@TableField("target_bean")
@ApiModelProperty(example = "helloTask")
private String targetBean;
/**
* 执行方法
*/
@TableField("trget_method")
@ApiModelProperty(example = "executeMethod")
private String trgetMethod;
/**
* 执行参数
*/
@ApiModelProperty(example = "Hello-Word!")
private String params;
/**
* 任务类型-
*/
@TableField("quartz_type")
@ApiModelProperty(example = "API_GROUPS")
private String quartzType;
/**
* 任务状态 0:正常 1:暂停
*/
@ApiModelProperty(example = "0")
private Integer status;
/**
* 创建者
*/
@TableField(value = "create_by", fill = FieldFill.INSERT)
@ApiModelProperty(hidden = true)
protected Long createId;
/**
* 创建日期
*/
@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;
/**
* 删除标记(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类-定时任务日志对象
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;
/**
* 实体编号(唯一标识)
*/
protected Long id;
/**
* 任务ID
*/
@TableField("job_id")
private Long jobId;
/**
* 定时任务名称
*/
private String name;
/**
* 定制任务执行类
*/
@TableField("target_bean")
private String targetBean;
/**
* 定时任务执行方法
*/
@TableField("trget_method")
private String trgetMethod;
/**
* 执行参数
*/
private String params;
/**
* 任务状态
*/
private Integer status;
/**
* 异常消息
*/
private String error;
/**
* 执行时间
*/
private Integer times;
/**
* 创建者
*/
@TableField(value = "create_by", fill = FieldFill.INSERT)
@ApiModelProperty(hidden = true)
protected Long createId;
/**
* 创建日期
*/
@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;
/**
* 删除标记(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类-定时任务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
* 定时任务 服务类
*
**/
public interface QuartzTaskService extends IService<QuartzTask> {
/**
* 根据ID,查询定时任务
*/
QuartzTask queryObject(Long jobId);
/**
* 分页查询定时任务列表
*/
Page<QuartzTask> queryList(EntityWrapper<QuartzTask> wrapper, Page<QuartzTask> page);
/**
* 保存定时任务
*/
void saveQuartzTask(QuartzTask quartzTask);
/**
* 更新定时任务
*/
void updateQuartzTask(QuartzTask quartzTask);
/**
* 批量删除定时任务
*/
void deleteBatchTasks(List<Long> ids);
/**
* 批量更新定时任务状态
*/
int updateBatchTasksByStatus(List<Long> ids, Integer status);
/**
* 立即执行
*/
void run(List<Long> jobIds);
/**
* 暂停运行
*/
void paush(List<Long> jobIds);
/**
* 恢复运行
*/
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
*
* 定时任务 服务实现类
* @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;
/**
* 项目启动时,初始化定时器
*/
@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());
//如果不存在,则创建
if (cronTrigger == null) {
LOGGER.info("Quartz创建定时任务成功,任务:" + scheduleJob);
ScheduleUtils.createScheduleJob(scheduler, scheduleJob);
} else {
LOGGER.info("Quartz更新定时任务成功,任务:" + 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创建定时任务成功,任务:" + quartzTask);
ScheduleUtils.createScheduleJob(scheduler, quartzTask);
}
@Override
public void updateQuartzTask(QuartzTask quartzTask) {
baseMapper.updateById(quartzTask);
LOGGER.info("Quartz更新定时任务成功,任务:" + quartzTask);
ScheduleUtils.updateScheduleJob(scheduler, quartzTask);
}
@Override
public void deleteBatchTasks(List<Long> ids) {
for (Long id : ids) {
LOGGER.info("Quartz批量删除定时任务成功,任务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批量立即运行定时任务成功,任务ID:" + jobId);
ScheduleUtils.run(scheduler, queryObject(jobId));
}
}
@Override
public void paush(List<Long> jobIds) {
for (Long jobId : jobIds) {
LOGGER.info("Quartz批量暂停运行定时任务成功,任务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批量恢复运行定时任务成功,任务ID:" + jobId);
ScheduleUtils.resumeJob(scheduler, jobId);
}
updateBatchTasksByStatus(jobIds, Constants.QUARTZ_STATUS_NOMAL);
}
}
六、QuartzTaskLogService和QuartzTaskLogServiceImpl类-定时任务日志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
* 任务执行日志 服务类
*
**/
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
* 任务执行日志 服务实现类
*
**/
@Service
@Transactional(rollbackFor = Exception.class)
public class QuartzTaskLogServiceImpl extends ServiceImpl<QuartzTaskLogDao, QuartzTaskLog> implements QuartzTaskLogService {
}
七、QuartzTaskController类-定时任务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
*
* 定时任务 前端控制器
*
* @author wangl
* @since 2018-01-24
*/
@RestController
@Api(tags = "QuartzTaskController",description = "定时任务模块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 = "定时任务列表",httpMethod = com.unnet.yjs.annotation.HttpMethod.GET)
@ApiImplicitParams({
@ApiImplicitParam(name = "p_page", value = "页数", defaultValue = "1", dataType = "Integer", paramType = "query"),
@ApiImplicitParam(name = "p_limit", value = "每页条数", 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 = "新增定时任务",httpMethod = HttpMethod.POST)
public RestResponse add(@RequestBody @ApiParam QuartzTask quartzTask){
quartzTaskService.saveQuartzTask(quartzTask);
return RestResponse.success();
}
@PostMapping("edit")
@ApiOperation(value = "更新定时任务",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 = "删除定时任务",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();
}
/**
* 暂停选中的定时任务
* @param ids 任务ID List
*/
@PostMapping("paush")
@ApiOperation(value = "暂停定时任务",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();
}
/**
* 恢复选中的定时任务运行
* @param ids 任务ID List
*/
@PostMapping("resume")
@ApiOperation(value = "恢复定时任务",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();
}
/**
* 立即执行选中的定时任务
* @param ids 任务ID List
*/
@PostMapping("run")
@ApiOperation(value = "运行定时任务",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类-定时任务日志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
*
*
* 任务执行日志 前端控制器
* @author wangl
* @since 2018-01-25
*/
@Controller
@Api(tags = "QuartzTaskLogController",description = "定时任务日志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相关类
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的功能描述:
* 定时任务
* @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();
//数据库保存执行记录
QuartzTaskLog log = new QuartzTaskLog();
log.setJobId(scheduleJob.getId());
log.setTargetBean(scheduleJob.getTargetBean());
log.setTrgetMethod(scheduleJob.getTrgetMethod());
log.setParams(param);
log.setName("执行定时任务【"+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());
//任务开始时间
long startTime = System.currentTimeMillis();
try {
//执行任务
logger.info("任务准备执行,任务ID:" + scheduleJob.getId());
ScheduleRunnable task = new ScheduleRunnable(scheduleJob.getTargetBean(),
scheduleJob.getTrgetMethod(), scheduleJob.getParams());
Future<?> future = service.get().submit(task);
future.get();
//任务执行总时长
long times = System.currentTimeMillis() - startTime;
log.setTimes((int)times);
//任务状态 0:成功 1:失败
log.setStatus(0);
logger.info("任务执行完毕,任务ID:" + scheduleJob.getId() + " 总共耗时:" + times + "毫秒");
} catch (Exception e) {
logger.error("任务执行失败,任务ID:" + scheduleJob.getId(), e);
//任务执行总时长
long times = System.currentTimeMillis() - startTime;
log.setTimes((int)times);
//任务状态 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的功能描述:
* 执行定时任务
* @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("执行定时任务失败", 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的功能描述:
* 定时任务工具类
* @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_";
/**
* 获取触发器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);
}
/**
* 获取表达式触发器
*/
public static CronTrigger getCronTrigger(Scheduler scheduler, Long jobId) {
try {
return (CronTrigger) scheduler.getTrigger(getTriggerKey(jobId));
} catch (SchedulerException e) {
throw new MyException("获取定时任务CronTrigger出现异常", e);
}
}
/**
* 创建定时任务
*/
public static void createScheduleJob(Scheduler scheduler, QuartzTask scheduleJob) {
try {
//构建job信息
JobDetail jobDetail = JobBuilder.newJob(ScheduleJob.class).withIdentity(getJobKey(scheduleJob.getId())).build();
//表达式调度构建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCron())
.withMisfireHandlingInstructionDoNothing();
//按新的cronExpression表达式构建一个新的trigger
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(scheduleJob.getId())).withSchedule(scheduleBuilder).build();
//放入参数,运行时的方法可以获取
jobDetail.getJobDataMap().put(QuartzTask.JOB_PARAM_KEY, scheduleJob);
scheduler.scheduleJob(jobDetail, trigger);
//暂停任务
if(scheduleJob.getStatus().intValue() == Constants.QUARTZ_STATUS_PUSH.intValue()){
pauseJob(scheduler, scheduleJob.getId());
}
} catch (SchedulerException e) {
throw new MyException("创建定时任务失败", e);
}
}
/**
* 更新定时任务
*/
public static void updateScheduleJob(Scheduler scheduler, QuartzTask scheduleJob) {
try {
TriggerKey triggerKey = getTriggerKey(scheduleJob.getId());
//表达式调度构建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCron())
.withMisfireHandlingInstructionDoNothing();
CronTrigger trigger = getCronTrigger(scheduler, scheduleJob.getId());
//按新的cronExpression表达式重新构建trigger
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
//参数
trigger.getJobDataMap().put(QuartzTask.JOB_PARAM_KEY, scheduleJob);
scheduler.rescheduleJob(triggerKey, trigger);
//暂停任务
if(scheduleJob.getStatus().intValue() == Constants.QUARTZ_STATUS_PUSH.intValue()){
pauseJob(scheduler, scheduleJob.getId());
}
} catch (SchedulerException e) {
throw new MyException("更新定时任务失败", e);
}
}
/**
* 立即执行任务
*/
public static void run(Scheduler scheduler, QuartzTask scheduleJob) {
try {
//参数
JobDataMap dataMap = new JobDataMap();
dataMap.put(QuartzTask.JOB_PARAM_KEY, scheduleJob);
scheduler.triggerJob(getJobKey(scheduleJob.getId()), dataMap);
} catch (SchedulerException e) {
throw new MyException("立即执行定时任务失败", e);
}
}
/**
* 暂停任务
*/
public static void pauseJob(Scheduler scheduler, Long jobId) {
try {
scheduler.pauseJob(getJobKey(jobId));
} catch (SchedulerException e) {
throw new MyException("暂停定时任务失败", e);
}
}
/**
* 恢复任务
*/
public static void resumeJob(Scheduler scheduler, Long jobId) {
try {
scheduler.resumeJob(getJobKey(jobId));
} catch (SchedulerException e) {
throw new MyException("暂停定时任务失败", e);
}
}
/**
* 删除定时任务
*/
public static void deleteScheduleJob(Scheduler scheduler, Long jobId) {
try {
scheduler.deleteJob(getJobKey(jobId));
} catch (SchedulerException e) {
throw new MyException("删除定时任务失败", e);
}
}
}
ScheduleUtils封装了Quartz的定时任务相关操作;ReflectionUtils封装了反射的相关操作;ScheduleRunnable类实现了Runable接口,重写run方法,使用反射来调用定时任务类的方法;ScheduleJob类重写执行方法,实现定时任务的自定义任务逻辑,定时任务会按间隔执行这个方法;HelloTask类具体的定时任务类和具体的执行方法;
十、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参数
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");
//延时启动
schedulerFactoryBean.setStartupDelay(20);
schedulerFactoryBean.setApplicationContextSchedulerContextKey("applicationContextKey");
//可选,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
schedulerFactoryBean.setOverwriteExistingJobs(true);
//设置自动启动,默认为true | 开启定时任务的开关
schedulerFactoryBean.setAutoStartup(true);
return schedulerFactoryBean;
}
}
数据源可以自己配置,属性名可以修改; schedulerFactoryBean.setAutoStartup(true);这个方法要高设为true,否则定时任务不会启动;
十一、测试
-
新增定时任务
- 定时任务执行日志
2019-07-14 15:00:25.781 INFO 25724 --- [nio-9080-exec-6] com.unnet.yjs.service.QuartzTaskService : Quartz创建定时任务成功,任务: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 : 任务准备执行,任务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 : 任务执行完毕,任务ID:9 总共耗时:23毫秒
2019-07-14 15:01:03.056 INFO 25724 --- [eduler_Worker-1] c.unnet.yjs.config.SysMetaObjectHandler : 正在调用该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, '执行定时任务【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 )