你了解 Quartz 吗?
Quartz 是一个完全由 Java 编写的开源作业调度框架,为在 Java 应用程序中进行作业调度提供了简单却强大的机制。
Quartz 可以与 J2EE 与 J2SE 应用程序相结合也可以单独使用。
Quartz 允许程序开发人员根据时间的间隔来调度作业。
Quartz 实现了作业和触发器的多对多的关系,还能把多个作业与不同的触发器关联。
Quartz 核心概念
我们需要明白 Quartz 的几个核心概念,这样理解起 Quartz 的原理就会变得简单了。
- Job 表示一个工作,要执行的具体内容。此接口中只有一个方法,如下:
void execute(JobExecutionContext context)
- JobDetail 表示一个具体的可执行的调度程序,Job 是这个可执行程调度程序所要执行的内容,另外 JobDetail 还包含了这个任务调度的方案和策略。
- Trigger 代表一个调度参数的配置,什么时候去调。
- Scheduler 代表一个调度容器,一个调度容器中可以注册多个 JobDetail 和 Trigger。当 Trigger 与 JobDetail 组合,就可以被 Scheduler 容器调度了。
Quartz的运行环境
- Quartz 可以运行嵌入在另一个独立式应用程序。
- Quartz 可以在应用程序服务器(或 servlet 容器)内被实例化,并且参与 XA 事务。
- Quartz 可以作为一个独立的程序运行(其自己的 Java 虚拟机内),可以通过 RMI 使用。
- Quartz 可以被实例化,作为独立的项目集群(负载平衡和故障转移功能),用于作业的执行。
业务代码
1.controller层
package com.csii.quartz.controller;
import com.csii.quartz.base.ResultCode;
import com.csii.quartz.service.ScheduleJobService;
import entities.ScheduleJobRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import java.util.List;
/**
* @author :renhuifeng
* @date :Created in 2020/8/11 11:28
* @description:
* @modified By:
* @version: $
*/
@RestController
@Api(tags = "定时器")
@Component
public class ScheduleJobController {
@Autowired
private ScheduleJobService scheduleJobService;
@GetMapping(value = "/schedulejobnacos")
public void getRegist() {
System.out.println("=====================测试任务调度getRegist============================");
}
@PostMapping(value = "/schedulejobsave")
@ApiOperation(value = "保存", notes = "保存")
public ResultCode save(ScheduleJobRequest job) {
scheduleJobService.saveJob(job);
return ResultCode.SUCCESS;
}
@PostMapping(value = "/schedulejobupdate")
@ApiOperation(value = "修改", notes = "修改")
public ResultCode update(ScheduleJobRequest job) {
scheduleJobService.updateJob(job);
return ResultCode.SUCCESS;
}
@GetMapping(value = "/schedulejobrun")
@ApiOperation(value = "执行任务", notes = "执行任务")
public ResultCode run(@RequestParam(required = true) @ApiParam(value="id",required = true) List<Long> jobIds) {
scheduleJobService.run(jobIds);
return ResultCode.SUCCESS;
}
@GetMapping(value = "/schedulejobdel")
@ApiOperation(value = "删除任务", notes = "删除任务")
public ResultCode del(@RequestParam(required = true) @ApiParam(value="id",required = true)List<Long> jobIds) {
scheduleJobService.deleteBatch(jobIds);
return ResultCode.SUCCESS;
}
@GetMapping(value = "/schedulejobpause")
@ApiOperation(value = "暂停任务", notes = "暂停任务")
public ResultCode pause(@RequestParam(required = true) @ApiParam(value="id",required = true)List<Long> jobIds) {
scheduleJobService.pause(jobIds);
return ResultCode.SUCCESS;
}
@GetMapping(value = "/schedulejobresume")
@ApiOperation(value = "恢复任务", notes = "恢复任务")
public ResultCode resume(@RequestParam(required = true) @ApiParam(value="id",required = true)List<Long> jobIds) {
scheduleJobService.resume(jobIds);
return ResultCode.SUCCESS;
}
}
2.service层
2.1 impl
ScheduleJobLogServiceImpl .java
package com.csii.quartz.service.impl;
import com.csii.quartz.dao.ScheduleJobLogMapper;
import com.csii.quartz.service.ScheduleJobLogService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import entities.ScheduleJobLog;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
/**
* @author :renhuifeng
* @date :Created in 2020/8/11 10:58
* @description:
* @modified By:
* @version: $
*/
@Service
public class ScheduleJobLogServiceImpl implements ScheduleJobLogService {
@Override
public PageInfo queryPage(Map<String, Object> params) {
/* Long jobId = Long.parseLong(params.get("jobId").toString());
Integer page = Integer.parseInt(params.get("page").toString());
Integer pageSize = Integer.parseInt(params.get("pageSize").toString());
PageHelper.startPage(page,pageSize);
ScheduleJobLogExample scheduleJobLogExample = new ScheduleJobLogExample();
scheduleJobLogExample.createCriteria().andJobIdEqualTo(jobId);
List<ScheduleJobLog> scheduleJobLogs = scheduleJobLogMapper.selectByExample(scheduleJobLogExample);*/
PageInfo pageInfo = new PageInfo<>();
return pageInfo;
}
}
ScheduleJobServiceImpl .java
package com.csii.quartz.service.impl;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.additional.query.impl.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.additional.query.impl.QueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.additional.update.impl.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.service.additional.update.impl.UpdateChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.csii.quartz.base.Constant;
import com.csii.quartz.dao.ScheduleJobMapper;
import com.csii.quartz.service.ScheduleJobService;
import com.csii.quartz.utils.ScheduleUtils;
import entities.ScheduleJob;
import entities.ScheduleJobRequest;
import org.quartz.CronTrigger;
import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
/**
* @author :renhuifeng
* @date :Created in 2020/8/11 10:55
* @description:
* @modified By:
* @version: $
*/
@Service
public class ScheduleJobServiceImpl extends ServiceImpl<ScheduleJobMapper, ScheduleJob> implements ScheduleJobService {
@Autowired
private Scheduler scheduler;
@Autowired
private ScheduleJobMapper scheduleJobMapper;
/**
* 项目启动时,初始化定时器
*/
@PostConstruct
public void init(){
List<ScheduleJob> scheduleJobList = this.baseMapper.selectAll();
for(ScheduleJob scheduleJob : scheduleJobList){
CronTrigger cronTrigger = ScheduleUtils.getCronTrigger(scheduler, scheduleJob.getJobId());
//如果不存在,则创建
if(cronTrigger == null) {
ScheduleUtils.createScheduleJob(scheduler, scheduleJob);
}else {
ScheduleUtils.updateScheduleJob(scheduler, scheduleJob);
}
}
}
/* @Override
public PageInfo queryPage(Map<String, Object> params) {
String beanName = (String)params.get("beanName");
int page = Integer.parseInt(params.getOrDefault("page", "1").toString());
int pageSize = Integer.parseInt(params.getOrDefault("pageSize", "10").toString());
PageHelper.startPage(page,pageSize);
ScheduleJobExample scheduleJobExample = new ScheduleJobExample();
scheduleJobExample.createCriteria().andBeanNameLike(beanName);
List<ScheduleJob> scheduleJobs = scheduleJobMapper.selectByExample(scheduleJobExample);
PageInfo pageInfo = new PageInfo<>(scheduleJobs);
return pageInfo;
}*/
@Override
@Transactional(rollbackFor = Exception.class)
public void saveJob(ScheduleJobRequest scheduleJob) {
ScheduleJob entity = buildEntity(scheduleJob);
this.baseMapper.insert(entity);
ScheduleUtils.createScheduleJob(scheduler, entity);
}
@Override
public void updateJob(ScheduleJobRequest scheduleJob) {
ScheduleJob entity = buildEntity(scheduleJob);
ScheduleUtils.updateScheduleJob(scheduler, entity);
this.baseMapper.updateByPrimaryKey(entity);
}
/* @Override
@Transactional(rollbackFor = Exception.class)
public void save(ScheduleJob scheduleJob) {
scheduleJob.setCreateTime(new Date());
scheduleJob.setStatus(Byte.parseByte(Constant.NORMAL+""));
scheduleJobMapper.insertSelective(scheduleJob);
ScheduleUtils.createScheduleJob(scheduler, scheduleJob);
}*/
@Override
public boolean saveBatch(Collection<ScheduleJob> entityList) {
return false;
}
@Override
public boolean saveOrUpdateBatch(Collection<ScheduleJob> entityList) {
return false;
}
@Override
public boolean update(Wrapper<ScheduleJob> updateWrapper) {
return false;
}
@Override
public boolean updateBatchById(Collection<ScheduleJob> entityList) {
return false;
}
@Override
public ScheduleJob getOne(Wrapper<ScheduleJob> queryWrapper) {
return null;
}
@Override
public <V> V getObj(Wrapper<ScheduleJob> queryWrapper, Function<? super Object, V> mapper) {
return null;
}
@Override
public int count() {
return 0;
}
@Override
public List<ScheduleJob> list() {
return null;
}
@Override
public IPage<ScheduleJob> page(IPage<ScheduleJob> page) {
return null;
}
@Override
public List<Map<String, Object>> listMaps() {
return null;
}
@Override
public List<Object> listObjs() {
return null;
}
@Override
public <V> List<V> listObjs(Function<? super Object, V> mapper) {
return null;
}
@Override
public List<Object> listObjs(Wrapper<ScheduleJob> queryWrapper) {
return null;
}
@Override
public IPage<Map<String, Object>> pageMaps(IPage<ScheduleJob> page) {
return null;
}
@Override
public QueryChainWrapper<ScheduleJob> query() {
return null;
}
@Override
public LambdaQueryChainWrapper<ScheduleJob> lambdaQuery() {
return null;
}
@Override
public UpdateChainWrapper<ScheduleJob> update() {
return null;
}
@Override
public LambdaUpdateChainWrapper<ScheduleJob> lambdaUpdate() {
return null;
}
/* @Override
@Transactional(rollbackFor = Exception.class)
public void update(ScheduleJob scheduleJob) {
ScheduleUtils.updateScheduleJob(scheduler, scheduleJob);
scheduleJobMapper.updateByPrimaryKeySelective(scheduleJob);
}*/
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteBatch(List<Long> jobIds) {
for(Long jobId : jobIds){
ScheduleUtils.deleteScheduleJob(scheduler, jobId);
}
}
@Override
public int updateBatch(List<Long> jobIds, int status){
for(Long jobId : jobIds){
ScheduleJob record = new ScheduleJob();
record.setJobId(jobId);
record.setStatus(status);
scheduleJobMapper.updateBatch( record);
}
return 1;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void run(List<Long> jobIds) {
for(Long jobId : jobIds){
ScheduleUtils.run(scheduler, scheduleJobMapper.selectByPrimaryKey(jobId));
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void pause(List<Long> jobIds) {
for(Long jobId : jobIds){
ScheduleUtils.pauseJob(scheduler, jobId);
}
updateBatch(jobIds, Constant.PAUSE);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void resume(List<Long> jobIds) {
for(Long jobId : jobIds){
ScheduleUtils.resumeJob(scheduler, jobId);
}
updateBatch(jobIds, Constant.NORMAL);
}
private ScheduleJob buildEntity(ScheduleJobRequest req){
ScheduleJob entity = new ScheduleJob();
if(req.getJobId() != null){
entity.setJobId(req.getJobId());
}else{
entity.setCreateTime(new Date());
}
entity.setStatus(Constant.NORMAL);
entity.setCronExpression(req.getCron());
entity.setMethodName(req.getMethodName());
entity.setBeanName(req.getName());
entity.setParams(req.getParams());
return entity;
}
}
2.2 service接口
ScheduleJobService.java
package com.csii.quartz.service;
import com.github.pagehelper.PageInfo;
import entities.ScheduleJob;
import entities.ScheduleJobRequest;
import java.util.List;
import java.util.Map;
/**
* @author :renhuifeng
* @date :Created in 2020/8/11 10:50
* @description:定时任务
* @modified By:
* @version: $
*/
public interface ScheduleJobService {
/* PageInfo queryPage(Map<String, Object> params);*/
/**
* 保存定时任务
*/
void saveJob(ScheduleJobRequest scheduleJob);
/**
* 更新定时任务
*/
void updateJob(ScheduleJobRequest scheduleJob);
/**
* 批量删除定时任务
*/
void deleteBatch(List<Long> jobIds);
/**
* 批量更新定时任务状态
*/
int updateBatch(List<Long> jobIds, int status);
/**
* 立即执行
*/
void run(List<Long> jobIds);
/**
* 暂停运行
*/
void pause(List<Long> jobIds);
/**
* 恢复运行
*/
void resume(List<Long> jobIds);
}
ScheduleJobLogService.java
package com.csii.quartz.service;
import com.github.pagehelper.PageInfo;
import java.util.Map;
/**
* @author :renhuifeng
* @date :Created in 2020/8/11 10:54
* @description:定时任务日志
* @modified By:
* @version: 1.0$
*/
public interface ScheduleJobLogService {
PageInfo queryPage(Map<String, Object> params);
}
3.dao层
ScheduleJobLogMapper.java
package com.csii.quartz.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import entities.ScheduleJobLog;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* @author :renhuifeng
* @date :Created in 2020/8/11 11:12
* @description:
* @modified By:
* @version: $
*/
@Mapper
@Repository
public interface ScheduleJobLogMapper extends BaseMapper<ScheduleJobLog> {
/* long countByExample(ScheduleJobLogExample example);
int deleteByExample(ScheduleJobLogExample example);*/
int deleteByPrimaryKey(Long logId);
int insert(ScheduleJobLog record);
int insertSelective(ScheduleJobLog record);
ScheduleJobLog selectByPrimaryKey(Long logId);
int updateByPrimaryKeySelective(ScheduleJobLog record);
int updateByPrimaryKey(ScheduleJobLog record);
}
ScheduleJobMapper.java
package com.csii.quartz.dao;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import entities.ScheduleJob;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;
import java.util.List;
@Mapper
@Repository
public interface ScheduleJobMapper extends BaseMapper<ScheduleJob> {
int deleteByPrimaryKey(Long jobId);
int insert(ScheduleJob record);
int insertSelective(ScheduleJob record);
ScheduleJob selectByPrimaryKey(Long jobId);
int updateByPrimaryKeySelective(ScheduleJob record);
int updateByPrimaryKey(ScheduleJob record);
int updateBatch(ScheduleJob record);
List<ScheduleJob> selectAll();
}
4.entits实体类
ScheduleJobRequest.java 保存修改时的参数
package entities;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author :renhuifeng
* @date :Created in 2020/8/11 12:04
* @description:保存修改时的参数
* @modified By:
* @version: $
*/
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ScheduleJobRequest {
@ApiModelProperty(value = "id(修改时为必须)")
private Long JobId;
@ApiModelProperty(value = "spring bean名称(大喇叭定时器默认horn)", required = true)
private String name;
@ApiModelProperty(value = "方法名(大喇叭定时器默认horn)", required = true)
private String methodName;
@ApiModelProperty(value = "参数(备注)", required = true)
private String params;
@ApiModelProperty(value = "定时器执行计划(0/10 * * * * ? 每隔10秒一次)", required = true)
private String cron;
}
ScheduleJobLog.java
package entities;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import java.util.Date;
@AllArgsConstructor
@NoArgsConstructor
public class ScheduleJobLog {
private Long logId;
private Long jobId;
private String beanName;
private String methodName;
private String params;
private Byte status;
private String error;
private Integer times;
private Date createTime;
public Long getLogId() {
return logId;
}
public void setLogId(Long logId) {
this.logId = logId;
}
public Long getJobId() {
return jobId;
}
public void setJobId(Long jobId) {
this.jobId = jobId;
}
public String getBeanName() {
return beanName;
}
public void setBeanName(String beanName) {
this.beanName = beanName == null ? null : beanName.trim();
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName == null ? null : methodName.trim();
}
public String getParams() {
return params;
}
public void setParams(String params) {
this.params = params == null ? null : params.trim();
}
public Byte getStatus() {
return status;
}
public void setStatus(Byte status) {
this.status = status;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error == null ? null : error.trim();
}
public Integer getTimes() {
return times;
}
public void setTimes(Integer times) {
this.times = times;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
}
5.exception自定义异常
GenericException.java
package com.csii.quartz.exception;
/**
* @author :renhuifeng
* @date :Created in 2020/8/10 17:33
* @description:自定义异常
* @modified By:
* @version: 1.0$
*/
public class GenericException extends RuntimeException{
private static final long serialVersionUID = 1L;
private String msg;
private int code = 500;
public GenericException(String msg) {
super(msg);
this.msg = msg;
}
public GenericException(String msg, Throwable e) {
super(msg, e);
this.msg = msg;
}
public GenericException(String msg, int code) {
super(msg);
this.msg = msg;
this.code = code;
}
public GenericException(String msg, int code, Throwable e) {
super(msg, e);
this.msg = msg;
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
}
GenericExceptionHandler.java
package com.csii.quartz.exception;
import com.csii.quartz.base.ResultDTO;
import com.csii.quartz.utils.ResultUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;
/**
* @author :renhuifeng
* @date :Created in 2020/8/10 17:47
* @description:异常处理器
* @modified By:
* @version: $
*/
@RestControllerAdvice
@Slf4j
public class GenericExceptionHandler {
/**
* 处理自定义异常
*/
@ExceptionHandler(GenericException.class)
public ResultDTO handleRRException(GenericException e){
if (e instanceof GenericException) {
GenericException e1 = (GenericException) e;
log.error(e1.getCode()+"");
return ResultUtils.getResultDTO(e1.getCode(), e1.getMessage(), null);
}
log.error(e.getCause().getMessage());
log.error(e.getMessage());
return ResultUtils.getFail(null);
}
@ExceptionHandler(NoHandlerFoundException.class)
public ResultDTO handlerNoFoundException(java.lang.Exception e) {
log.error(e.getMessage(), e);
return ResultUtils.getResultDTO(404, "路径不存在,请检查路径是否正确",null);
}
@ExceptionHandler(DuplicateKeyException.class)
public ResultDTO handleDuplicateKeyException(DuplicateKeyException e){
log.error(e.getMessage(), e);
return ResultUtils.getResultDTO(99,"数据库中已存在该记录",null);
}
@ExceptionHandler(java.lang.Exception.class)
public ResultDTO handleException(java.lang.Exception e){
log.error(e.getMessage(), e);
return ResultUtils.getFail(null);
}
}
6.utils工具类
ResultUtils.java 返回信息工具
package com.csii.quartz.utils;
import com.csii.quartz.base.ResultCode;
import com.csii.quartz.base.ResultDTO;
/**
* @author :renhuifeng
* @date :Created in 2020/8/11 10:18
* @description:返回信息工具
* @modified By:
* @version: 1.0$
*/
public class ResultUtils {
private ResultUtils() {
}
public static ResultDTO getSuccess(Object result) {
return new ResultDTO(ResultCode.SUCCESS, result);
}
public static ResultDTO getFail(Object result) {
return new ResultDTO(ResultCode.FAIL, result);
}
public static ResultDTO getParamError(Object result) {
return new ResultDTO(ResultCode.PARAM_ERROR, result);
}
public static ResultDTO getResourceInvalid(Object result) {
return new ResultDTO(ResultCode.RESOURCE_INVALID, result);
}
public static ResultDTO getTokenInvalid(Object result) {
return new ResultDTO(ResultCode.TOKEN_INVALID, result);
}
public static ResultDTO getCustomErrordesc(String errorDesc, Object result) {
return new ResultDTO(ResultCode.CUSTOM_ERRORDESC, errorDesc, result);
}
public static ResultDTO getResultDTO(int code, String errorDesc, Object result) {
return new ResultDTO(code, errorDesc, result);
}
}
ScheduleJobUtils.java 定时任务工具
package com.csii.quartz.utils;
import com.csii.quartz.base.Constant;
import com.csii.quartz.dao.ScheduleJobLogMapper;
import entities.ScheduleJob;
import entities.ScheduleJobLog;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* @author :renhuifeng
* @date :Created in 2020/8/11 10:21
* @description:定时任务工具
* @modified By:
* @version: 1.0$
*/
@Slf4j
@Component
public class ScheduleJobUtils extends QuartzJobBean {
//返回一个线程池(这个线程池只有一个线程),这个线程
//池可以在线程死后(或发生异常时)重新启动一个线程来替代原来的线程继续执行下去!
private ExecutorService service = Executors.newSingleThreadExecutor();
@Autowired
private ScheduleJobLogMapper scheduleJobLogMapper;
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
ScheduleJob scheduleJob = (ScheduleJob ) context.getMergedJobDataMap()
.get(Constant.JOB_PARAM_KEY);
//获取spring bean
// ScheduleJobLogService scheduleJobLogService = (ScheduleJobLogService) SpringContextUtils.getBean("scheduleJobLogService");
//数据库保存执行记录
ScheduleJobLog jobLog = new ScheduleJobLog();
jobLog.setJobId(scheduleJob.getJobId());
jobLog.setBeanName(scheduleJob.getBeanName());
jobLog.setMethodName(scheduleJob.getMethodName());
jobLog.setParams(scheduleJob.getParams());
jobLog.setCreateTime(new Date());
//任务开始时间
long startTime = System.currentTimeMillis();
Byte zero = 0;
Byte one=1;
try {
//执行任务
log.info("任务准备执行,任务ID:" + scheduleJob.getJobId());
ScheduleRunnable task = new ScheduleRunnable(scheduleJob.getBeanName(),
scheduleJob.getMethodName(), scheduleJob.getParams());
Future<?> future = service.submit(task);
future.get();
//任务执行总时长
long times = System.currentTimeMillis() - startTime;
jobLog.setTimes((int)times);
//任务状态 0:成功 1:失败
jobLog.setStatus(zero);
log.info("任务执行完毕,任务ID:" + scheduleJob.getJobId() + " 总共耗时:" + times + "毫秒");
} catch (Exception e) {
log.error("任务执行失败,任务ID:" + scheduleJob.getJobId(), e);
//任务执行总时长
long times = System.currentTimeMillis() - startTime;
jobLog.setTimes((int)times);
//任务状态 0:成功 1:失败
jobLog.setStatus(one);
jobLog.setError(StringUtils.substring(e.toString(), 0, 2000));
}finally {
scheduleJobLogMapper.insertSelective(jobLog);
}
}
}
ScheduleRunnable.java 执行定时任务实现
package com.csii.quartz.utils;
import java.lang.reflect.Method;
import com.csii.quartz.exception.GenericException;
import org.apache.commons.lang.StringUtils;
import org.springframework.util.ReflectionUtils;
/**
* @author :renhuifeng
* @date :Created in 2020/8/11 10:27
* @description:执行定时任务实现
* @modified By:
* @version: 1.0$
*/
public class ScheduleRunnable implements Runnable{
private Object target;
private Method method;
private String params;
public ScheduleRunnable(String beanName, String methodName, String params) throws NoSuchMethodException, SecurityException {
this.target = SpringContextUtils.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 (java.lang.Exception e) {
throw new GenericException("执行定时任务失败", e);
}
}
}
ScheduleUtils.java 定时任务工具类
package com.csii.quartz.utils;
import com.csii.quartz.base.Constant;
import com.csii.quartz.exception.GenericException;
import entities.ScheduleJob;
import org.quartz.*;
/**
* @author :renhuifeng
* @date :Created in 2020/8/11 10:31
* @description:定时任务工具类
* @modified By:
* @version: 1.0$
*/
public class ScheduleUtils {
private final static String JOB_NAME = "TASK_";
/**
* 获取触发器key
*/
public static TriggerKey getTriggerKey(Long jobId) {
return TriggerKey.triggerKey(JOB_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 GenericException("获取定时任务CronTrigger出现异常", e);
}
}
/**
* 创建定时任务
*/
public static void createScheduleJob(Scheduler scheduler, ScheduleJob scheduleJob) {
try {
//构建job信息
JobDetail jobDetail = JobBuilder.newJob(ScheduleJobUtils.class).withIdentity(getJobKey(scheduleJob.getJobId())).build();
//表达式调度构建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression())
.withMisfireHandlingInstructionDoNothing();
//按新的cronExpression表达式构建一个新的trigger
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(scheduleJob.getJobId())).withSchedule(scheduleBuilder).build();
//放入参数,运行时的方法可以获取
jobDetail.getJobDataMap().put(Constant.JOB_PARAM_KEY, scheduleJob);
scheduler.scheduleJob(jobDetail, trigger);
//暂停任务
if(scheduleJob.getStatus() == Constant.PAUSE){
pauseJob(scheduler, scheduleJob.getJobId());
}
} catch (SchedulerException e) {
throw new GenericException("创建定时任务失败", e);
}
}
/**
* 更新定时任务
*/
public static void updateScheduleJob(Scheduler scheduler, ScheduleJob scheduleJob) {
try {
TriggerKey triggerKey = getTriggerKey(scheduleJob.getJobId());
//表达式调度构建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression())
.withMisfireHandlingInstructionDoNothing();
CronTrigger trigger = getCronTrigger(scheduler, scheduleJob.getJobId());
//按新的cronExpression表达式重新构建trigger
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
//参数
trigger.getJobDataMap().put(Constant.JOB_PARAM_KEY, scheduleJob);
scheduler.rescheduleJob(triggerKey, trigger);
//暂停任务
if(scheduleJob.getStatus() == Constant.PAUSE){
pauseJob(scheduler, scheduleJob.getJobId());
}
} catch (SchedulerException e) {
throw new GenericException("更新定时任务失败", e);
}
}
/**
* 立即执行任务
*/
public static void run(Scheduler scheduler, ScheduleJob scheduleJob) {
try {
//参数
JobDataMap dataMap = new JobDataMap();
dataMap.put(Constant.JOB_PARAM_KEY, scheduleJob);
scheduler.triggerJob(getJobKey(scheduleJob.getJobId()), dataMap);
} catch (SchedulerException e) {
throw new GenericException("立即执行定时任务失败", e);
}
}
/**
* 暂停任务
*/
public static void pauseJob(Scheduler scheduler, Long jobId) {
try {
scheduler.pauseJob(getJobKey(jobId));
} catch (SchedulerException e) {
throw new GenericException("暂停定时任务失败", e);
}
}
/**
* 恢复任务
*/
public static void resumeJob(Scheduler scheduler, Long jobId) {
try {
scheduler.resumeJob(getJobKey(jobId));
} catch (SchedulerException e) {
throw new GenericException("恢复定时任务失败", e);
}
}
/**
* 删除定时任务
*/
public static void deleteScheduleJob(Scheduler scheduler, Long jobId) {
try {
scheduler.deleteJob(getJobKey(jobId));
} catch (SchedulerException e) {
throw new GenericException("删除定时任务失败", e);
}
}
}
SpringContextUtils.java Context 工具类
package com.csii.quartz.utils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* @author :renhuifeng
* @date :Created in 2020/8/11 10:33
* @description:Spring Context 工具类
* @modified By:
* @version: $
*/
@Component
public class SpringContextUtils implements ApplicationContextAware {
public static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
SpringContextUtils.applicationContext = applicationContext;
}
public static Object getBean(String name) {
return applicationContext.getBean(name);
}
public static <T> T getBean(String name, Class<T> requiredType) {
return applicationContext.getBean(name, requiredType);
}
public static boolean containsBean(String name) {
return applicationContext.containsBean(name);
}
public static boolean isSingleton(String name) {
return applicationContext.isSingleton(name);
}
public static Class<? extends Object> getType(String name) {
return applicationContext.getType(name);
}
}
7.base 通用、枚举定义
Constant.java 常量
package com.csii.quartz.base;
/**
* @author :renhuifeng
* @date :Created in 2020/8/10 17:03
* @description:常量
* @modified By:
* @version: 1.0$
*/
public class Constant {
/**
* 任务调度参数key
*/
public static final String JOB_PARAM_KEY = "JOB_PARAM_KEY";
/**
* 定时任务状态
*
*/
/**
* 正常
*/
public static final int NORMAL = 0;
/**
* 暂停
*/
public static final int PAUSE =1;
/**
* 删除
*/
public static final int DEL =-1;
}
ResultCode.java 返回信息码
package com.csii.quartz.base;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author :renhuifeng
* @date :Created in 2020/8/10 17:04
* @description:返回信息码
* @modified By:
* @version: 1.0$
*/
@Getter
@AllArgsConstructor
public enum ResultCode {
SUCCESS(0,"操作成功"),
FAIL(1, "操作失败,请联系管理员"),
PARAM_ERROR(2, "参数值格式有误"),
RESOURCE_INVALID(3, "资源无效"),
TOKEN_INVALID(11, "授权码无效"),
CUSTOM_ERRORDESC(99);
private int Code;
private String ErrorDesc;
/**
* 消息
*/
private String msg;
ResultCode(int code, String errorDesc) {
Code = code;
ErrorDesc = errorDesc;
}
ResultCode(int code) {
Code = code;
}
public int getCode() {
return Code;
}
public void setCode(int code) {
Code = code;
}
public String getErrorDesc() {
return ErrorDesc;
}
public void setErrorDesc(String errorDesc) {
ErrorDesc = errorDesc;
}
}
ResultDTO.java 返回数据格式
package com.csii.quartz.base;
import java.io.Serializable;
/**
* @author :renhuifeng
* @date :Created in 2020/8/10 17:05
* @description:返回数据格式
* @modified By:
* @version: 1.0$
*/
public class ResultDTO implements Serializable {
private int code;
private String msg;
private Object data;
private int count;
public ResultDTO(ResultCode code, Object result) {
this.code = code.getCode();
this.msg = code.getErrorDesc();
this.data = result;
}
public ResultDTO(ResultCode code, String errorDesc, Object result) {
this.code = code.getCode();
this.msg = errorDesc;
this.data = result;
}
public ResultDTO(int code, String errorDesc, Object result) {
this.code = code;
this.msg = errorDesc;
this.data = result;
}
}
8.表结构
9.maven依赖
需要表结构与maven依赖的评论留言