1.添加pom依賴
<dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.0</version> <!-- quartz默認使用c3p0連接池,如果項目使用的不是則需要排除依賴包 --> <exclusions> <exclusion> <artifactId>c3p0</artifactId> <groupId>c3p0</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> </dependency>
2.添加QuartzConfig配置類
package one.stand.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.quartz.SchedulerFactoryBean; import com.alibaba.druid.pool.DruidDataSource; import java.util.Properties; /** * 定時任務配置 * */ @Configuration public class QuartzConfig { //@Qualifier("druidDataSource") DataSource dataSource //DataSource 根據項目的數據源進行切換,我這里使用的是druidDataSource @Bean public SchedulerFactoryBean scheduler(DruidDataSource druidDataSource) { //quartz參數 Properties prop = new Properties(); //配置實例 //prop.put("org.quartz.scheduler.instanceName", "MyScheduler");//實例名稱 prop.put("org.quartz.scheduler.instanceId", "AUTO"); //線程池配置 prop.put("org.quartz.threadPool.threadCount", "5"); //JobStore配置 prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX"); prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_"); SchedulerFactoryBean factory = new SchedulerFactoryBean(); factory.setDataSource(druidDataSource); factory.setQuartzProperties(prop); factory.setSchedulerName("MyScheduler");//數據庫中存儲的名字 //QuartzScheduler 延時啟動,應用啟動5秒后 QuartzScheduler 再啟動 factory.setStartupDelay(5); //factory.setApplicationContextSchedulerContextKey("applicationContextKey"); //可選,QuartzScheduler 啟動時更新己存在的Job,這樣就不用每次修改targetObject后刪除qrtz_job_details表對應記錄了 factory.setOverwriteExistingJobs(true); //設置自動啟動,默認為true factory.setAutoStartup(true); return factory; } }
3.quartz常量及工具類
3.1 Constant常量類
package one.stand.task; /** * 常量 * */ public class Constant { /** 超級管理員ID */ public static final int SUPER_ADMIN = 1; /** * 菜單類型 * */ public enum MenuType { /** * 目錄 */ CATALOG(0), /** * 菜單 */ MENU(1), /** * 按鈕 */ BUTTON(2); private int value; MenuType(int value) { this.value = value; } public int getValue() { return value; } } /** * 定時任務狀態 * */ public enum ScheduleStatus { /** * 正常 */ NORMAL(0), /** * 暫停 */ PAUSE(1); private int value; ScheduleStatus(int value) { this.value = value; } public int getValue() { return value; } } /** * 雲服務商 */ public enum CloudService { /** * 七牛雲 */ QINIU(1), /** * 阿里雲 */ ALIYUN(2), /** * 騰訊雲 */ QCLOUD(3); private int value; CloudService(int value) { this.value = value; } public int getValue() { return value; } } }
3.2 SpringContextUtils工具類
package one.stand.task.util; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; /** * SpringContext工具類 * @author zgc * */ @Component public class SpringContextUtils implements ApplicationContextAware{ private static ApplicationContext applicationContext; /** * 實現ApplicationContextAware接口的context注入函數, 將其存入靜態變量. */ public void setApplicationContext(ApplicationContext applicationContext) { SpringContextUtils.applicationContext = applicationContext; // NOSONAR } /** * 取得存儲在靜態變量中的ApplicationContext. */ public static ApplicationContext getApplicationContext() { checkApplicationContext(); return applicationContext; } /** * 清除applicationContext靜態變量. */ public static void cleanApplicationContext() { applicationContext = null; } private static void checkApplicationContext() { if (applicationContext == null) { throw new IllegalStateException("applicaitonContext未注入,請在applicationContext.xml中定義SpringContextHolder"); } } /** * 從靜態變量ApplicationContext中取得Bean, 自動轉型為所賦值對象的類型. */ @SuppressWarnings("unchecked") public static <T> T getBean(String name) { checkApplicationContext(); return (T) applicationContext.getBean(name); } /** * 從靜態變量ApplicationContext中取得Bean, 自動轉型為所賦值對象的類型. */ @SuppressWarnings("unchecked") public static <T> T getBean(Class<T> clazz) { checkApplicationContext(); return (T) applicationContext.getBeansOfType(clazz); } }
3.3 SchedulerUtils工具類
package one.stand.task.util; import com.alibaba.fastjson.JSON; import one.stand.entity.ScheduleJob; import one.stand.exception.RRException; import one.stand.task.QuartzJob; import org.quartz.*; /** * quartz任務工具類 * @author zgc * */ public class SchedulerUtils { /** * 創建任務 * @throws RRException */ public static void createJob(Scheduler scheduler, ScheduleJob scheduleJob) throws RRException { try { Long jobId = scheduleJob.getJobId(); //創建Job對象 JobDetail job = JobBuilder.newJob(QuartzJob.class).withIdentity("JOB_" + jobId).build(); //獲取cron表達式 並創建對象 CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression()) .withMisfireHandlingInstructionDoNothing(); //創建觸發器 CronTrigger trigger = TriggerBuilder.newTrigger() .withIdentity("TRIGGET_" + jobId) .withSchedule(cronScheduleBuilder) //將cron表達式配置到觸發器 .build(); //將對象josn序列化存儲到Job的getJobDataMap()方法中,為后續根據獲取屬性執行對應的類的任務 job.getJobDataMap().put("JOB_PARAM_KEY", JSON.toJSONString(scheduleJob)); //存數據 scheduler.scheduleJob(job, trigger); scheduler.pauseJob(JobKey.jobKey("JOB_" + jobId));//使任務處於等待狀態,創建后不會執行 } catch (SchedulerException e) { throw new RRException("創建任務失敗", e); } } /** * 更新任務 * @throws RRException */ public static void updateJob(Scheduler scheduler, ScheduleJob scheduleJob) throws RRException { //獲取新的cron表達式 CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression()) .withMisfireHandlingInstructionDoNothing(); Long jobId = scheduleJob.getJobId(); try { //拿到原有的trigger TriggerKey triggerKey = TriggerKey.triggerKey("TRIGGER_" + jobId); CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); //為原有的trigger賦予新的cron表達式 trigger = trigger.getTriggerBuilder().withIdentity(triggerKey) .withSchedule(cronScheduleBuilder).build(); //執行原有的trigger更新 scheduler.rescheduleJob(triggerKey, trigger); } catch (SchedulerException e) { e.printStackTrace(); throw new RRException("更新定時任務失敗", e); } } /** * 刪除任務 * @throws RRException */ public static void deleteJob(Scheduler scheduler, Long jobId) throws RRException { try { scheduler.deleteJob(JobKey.jobKey("JOB_" + jobId)); } catch (SchedulerException e) { e.printStackTrace(); throw new RRException("刪除定時任務失敗", e); } } /** * 恢復任務 * @throws RRException */ public static void resumeJob(Scheduler scheduler, Long jobId) throws RRException { try { scheduler.resumeJob(JobKey.jobKey("JOB_" + jobId)); } catch (SchedulerException e) { e.printStackTrace(); throw new RRException("恢復定時任務失敗", e); } } /** * 立即執行定時任務 * @throws RRException */ public static void run(Scheduler scheduler, Long jobId) throws RRException { try { //只執行一次並且不會改變任務的狀態 scheduler.triggerJob(JobKey.jobKey("JOB_" + jobId)); } catch (SchedulerException e) { e.printStackTrace(); throw new RRException("立即執行定時任務失敗", e); } } /** * 暫停任務 * * @param scheduler * @param jobId * @throws RRException */ public static void pauseJob(Scheduler scheduler, Long jobId) throws RRException { try { scheduler.pauseJob(JobKey.jobKey("JOB_" + jobId)); } catch (SchedulerException e) { e.printStackTrace(); throw new RRException("暫停定時任務失敗", e); } } public static void main(String[] args) { System.out.println(org.quartz.CronExpression.isValidExpression("*/5 * * * * ?")); } }
3.4 CronUtils工具類
package one.stand.task.util; import java.text.ParsePosition; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Objects; /** * cron表達式生成工具類 * @author zgc * */ public class CronUtils { private static final SimpleDateFormat sdf = new SimpleDateFormat("ss mm HH dd MM ? yyyy"); /*** * 功能描述:日期轉換cron表達式 * @param date * @return */ public static String formatDateByPattern(Date date) { String formatTimeStr = null; if (Objects.nonNull(date)) { formatTimeStr = sdf.format(date); } return formatTimeStr; } /*** * convert Date to cron, eg "0 07 10 15 1 ? 2016" * @param date : 時間點 * @return */ public static String getCron(Date date) { return formatDateByPattern(date); } /** * 將長時間格式字符串轉換為時間 yyyy-MM-dd HH:mm:ss * * @param strDate * @return */ public static Date strToDateLong(String strDate) { SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); ParsePosition pos = new ParsePosition(0); Date strtodate = formatter.parse(strDate, pos); return strtodate; } /** * 根據時間字符串生成cron表達式 * @param dateStr * @return */ public static String getCronByDateStr(String dateStr) { Date date = strToDateLong(dateStr); return getCron(date); } public static void main(String[] args) { System.out.println(getCronByDateStr("2021-09-24 15:10:06")); } }
4.quartz任務及任務日志實體(sql文件見文末附件)
4.1 ScheduleJob實體類
package one.stand.entity; import java.io.Serializable; import java.util.Date; import lombok.Data; @Data public class ScheduleJob implements Serializable { private static final long serialVersionUID = 1L; private Long jobId; private Integer taskId; private String beanName; //執行的類名 private String methodName; //方法名 private String params; //參數 private String cronExpression; //cron表達式 private Integer status; //任務狀態 0,運行 1,暫停 private String remark; //備注 private Date createTime; //創建時間 }
4.2 ScheduleJobLog實體類
package one.stand.entity; import java.util.Date; import lombok.Data; @Data public class ScheduleJobLog { //日志ID字符串,逗號間隔 private Long logId; //日志ID字符串,逗號間隔 private String logIds; //任務ID private Long jobId; //spring bean名稱 private String beanName; //方法名 private String methodName; //參數 private String params; //失敗信息 private String error; //狀態;0=正常,1=暫停 private Integer status; //耗時(單位:毫秒) private Long times; //開始時間 private transient String beginTime; //結束時間 private transient String endTime; //創建時間 private Date createTime; }
5.quartz任務及任務日志Mapper(未使用mybatis可跳過)
5.1 ScheduleJobMapper.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="one.stand.mapper.ScheduleJobMapper"> <resultMap id="BaseResultMap" type="one.stand.entity.ScheduleJob"> <id column="job_id" jdbcType="BIGINT" property="jobId"/> <result column="task_id" jdbcType="INTEGER" property="taskId"/> <result column="bean_name" jdbcType="VARCHAR" property="beanName"/> <result column="method_name" jdbcType="VARCHAR" property="methodName"/> <result column="params" jdbcType="VARCHAR" property="params"/> <result column="cron_expression" jdbcType="VARCHAR" property="cronExpression"/> <result column="status" jdbcType="INTEGER" property="status"/> <result column="remark" jdbcType="VARCHAR" property="remark"/> <result column="create_time" jdbcType="TIMESTAMP" property="createTime"/> </resultMap> <sql id="Base_Column_List"> `job_id`, `task_id`, `bean_name`, `method_name`, `params`, `cron_expression`, `status`, `remark`, `create_time` </sql> <sql id="where"> <if test="taskId != null"> and task_id = #{taskId} </if> <if test="beanName != null"> and bean_name = #{beanName} </if> <if test="methodName != null"> and method_name = #{methodName} </if> <if test="status != null"> and status = #{status} </if> </sql> <select id="selectList" parameterType="one.stand.entity.ScheduleJob" resultMap="BaseResultMap"> select <include refid="Base_Column_List"/> from schedule_job <where> <include refid="where"/> </where> </select> <insert id="insertSelective" parameterType="one.stand.entity.ScheduleJob" keyProperty="jobId" useGeneratedKeys="true"> insert into schedule_job <trim prefix="(" suffix=")" suffixOverrides=","> <if test="jobId != null"> job_id, </if> <if test="taskId != null"> task_id, </if> <if test="beanName != null" > bean_name, </if> <if test="methodName != null" > method_name, </if> <if test="params != null" > params, </if> <if test="cronExpression != null" > cron_expression, </if> <if test="status != null" > status, </if> <if test="remark != null" > remark, </if> <if test="createTime != null" > create_time, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> <if test="jobId != null"> #{jobId,jdbcType=BIGINT}, </if> <if test="taskId != null"> #{taskId,jdbcType=INTEGER}, </if> <if test="beanName != null" > #{beanName,jdbcType=VARCHAR}, </if> <if test="methodName != null" > #{methodName,jdbcType=VARCHAR}, </if> <if test="params != null" > #{params,jdbcType=VARCHAR}, </if> <if test="cronExpression != null" > #{cronExpression,jdbcType=VARCHAR}, </if> <if test="status != null" > #{status,jdbcType=INTEGER}, </if> <if test="remark != null" > #{remark,jdbcType=VARCHAR}, </if> <if test="createTime != null" > #{createTime,jdbcType=TIMESTAMP}, </if> </trim> </insert> <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long" > select <include refid="Base_Column_List" /> from schedule_job where job_id = #{jobId,jdbcType=BIGINT} </select> <update id="updateByPrimaryKeySelective" parameterType="one.stand.entity.ScheduleJob" > update schedule_job <set> <if test="beanName != null" > bean_name = #{beanName,jdbcType=VARCHAR}, </if> <if test="methodName != null" > method_name = #{methodName,jdbcType=VARCHAR}, </if> <if test="params != null" > params = #{params,jdbcType=VARCHAR}, </if> <if test="cronExpression != null" > cron_expression = #{cronExpression,jdbcType=VARCHAR}, </if> <if test="status != null" > status = #{status,jdbcType=INTEGER}, </if> <if test="remark != null" > remark = #{remark,jdbcType=VARCHAR}, </if> <if test="createTime != null" > create_time = #{createTime,jdbcType=TIMESTAMP}, </if> </set> where job_id = #{jobId,jdbcType=BIGINT} </update> <!-- 批量刪除任務記錄 --> <delete id="deleteBatch"> delete from schedule_job where job_id in <foreach collection="array" item="jobId" open="(" separator="," close=")"> #{jobId} </foreach> </delete> <!-- 批量更新狀態 --> <update id="updateBatch"> update schedule_job set status = #{status} where job_id in <foreach collection="list" item="jobId" open="(" separator="," close=")"> #{jobId} </foreach> </update> </mapper>
5.2 ScheduleJobLogMapper.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="one.stand.mapper.ScheduleJobLogMapper"> <resultMap id="BaseResultMap" type="one.stand.entity.ScheduleJobLog"> <id column="log_id" jdbcType="BIGINT" property="logId"/> <result column="job_id" jdbcType="BIGINT" property="jobId"/> <result column="bean_name" jdbcType="VARCHAR" property="beanName"/> <result column="method_name" jdbcType="VARCHAR" property="methodName"/> <result column="params" jdbcType="VARCHAR" property="params"/> <result column="error" jdbcType="VARCHAR" property="error"/> <result column="status" jdbcType="INTEGER" property="status"/> <result column="times" jdbcType="BIGINT" property="times"/> <result column="begin_time" jdbcType="VARCHAR" property="beginTime"/> <result column="end_time" jdbcType="VARCHAR" property="endTime"/> <result column="create_time" jdbcType="TIMESTAMP" property="createTime"/> </resultMap> <sql id="Base_Column_List"> `log_id`, `job_id`, `bean_name`, `method_name`, `params`, `error`, `status`, `times`, `begin_time`, `end_time`, `create_time` </sql> <sql id="where"> <if test="status != null"> and status = #{status} </if> </sql> <select id="selectList" parameterType="one.stand.entity.ScheduleJobLog" resultMap="BaseResultMap"> select <include refid="Base_Column_List"/> from schedule_job_log <where> <include refid="where"/> </where> </select> <insert id="save" parameterType="one.stand.entity.ScheduleJobLog"> insert into schedule_job_log <trim prefix="(" suffix=")" suffixOverrides=","> <if test="logId != null"> log_id, </if> <if test="jobId != null"> job_id, </if> <if test="beanName != null" > bean_name, </if> <if test="methodName != null" > method_name, </if> <if test="params != null" > params, </if> <if test="error != null" > error, </if> <if test="status != null" > status, </if> <if test="times != null" > times, </if> <if test="beginTime != null" > begin_timme, </if> <if test="endTime != null" > end_timme, </if> <if test="createTime != null" > create_time, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> <if test="logId != null"> #{logId,jdbcType=BIGINT}, </if> <if test="jobId != null"> #{jobId,jdbcType=BIGINT}, </if> <if test="beanName != null" > #{beanName,jdbcType=VARCHAR}, </if> <if test="methodName != null" > #{methodName,jdbcType=VARCHAR}, </if> <if test="params != null" > #{params,jdbcType=VARCHAR}, </if> <if test="error != null" > #{error,jdbcType=VARCHAR}, </if> <if test="status != null" > #{status,jdbcType=INTEGER}, </if> <if test="times != null" > #{times,jdbcType=BIGINT}, </if> <if test="beginTime != null" > #{beginTime,jdbcType=VARCHAR}, </if> <if test="endTime != null" > #{endTime,jdbcType=VARCHAR}, </if> <if test="createTime != null" > #{createTime,jdbcType=TIMESTAMP}, </if> </trim> </insert> <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long" > select <include refid="Base_Column_List" /> from schedule_job_log where log_id = #{logId,jdbcType=BIGINT} </select> <update id="updateByPrimaryKey" parameterType="one.stand.entity.ScheduleJobLog" > update schedule_job_log <set> <if test="beanName != null" > bean_name = #{beanName,jdbcType=VARCHAR}, </if> <if test="methodName != null" > method_name = #{methodName,jdbcType=VARCHAR}, </if> <if test="params != null" > params = #{params,jdbcType=VARCHAR}, </if> <if test="status != null" > status = #{status,jdbcType=INTEGER}, </if> <if test="createTime != null" > create_time = #{createTime,jdbcType=TIMESTAMP}, </if> </set> where log_id = #{logId,jdbcType=BIGINT} </update> <!-- 批量刪除記錄 --> <delete id="deleteBatch"> delete from schedule_job_log where log_id in <foreach collection="logIds" item="logId" open="(" separator="," close=")"> #{logId} </foreach> </delete> </mapper>
6.quartz任務及任務日志接口
6.1 ScheduleJobService.java
package one.stand.service.task; import java.util.Map; import one.stand.entity.ScheduleJob; import one.stand.model.ResultModel; import one.stand.request.ScheduleJobRequest; public interface ScheduleJobService { /** * 查詢定時任務列表 * @param scheduleJobLog * @return */ ResultModel<Map<String, Object>> selectList(ScheduleJobRequest request); /** * 接口:保存定時任務 */ ResultModel<Boolean> save(ScheduleJob scheduleJob); /** * 查詢定時任務 * @param jobId * @return */ ResultModel<ScheduleJob> queryObject(Long jobId); /** * 修改定時任務 * @param scheduleJob * @return */ ResultModel<Boolean> update(ScheduleJob scheduleJob); /** * 刪除定時任務 * @param jobIds * @return */ ResultModel<Boolean> deleteBatch(Long[] jobIds); /** * 暫停定時任務 * @param jobIds * @return */ ResultModel<Boolean> pause(Long[] jobIds); /** * 恢復定時任務 * @param jobIds * @return */ ResultModel<Boolean> resume(Long[] jobIds); /** * 立即執行定時任務 * @param jobIds * @return */ ResultModel<Boolean> run(Long[] jobIds); }
6.2 ScheduleJobLogService.java
package one.stand.service.task; import java.util.Map; import one.stand.entity.ScheduleJobLog; import one.stand.request.ScheduleJobLogRequest; public interface ScheduleJobLogService { /** * 保存任務執行日志 * @param scheduleJobLog */ void save(ScheduleJobLog scheduleJobLog); /** * 查詢任務執行日志列表 * @param scheduleJobLog * @return */ Map<String, Object> selectList(ScheduleJobLogRequest request); /** * 查詢任務執行日志信息 * @param logId * @return */ ScheduleJobLog selectByPrimaryKey(Long logId); /** * 更新執行任務日志信息 * @param scheduleJobLog */ void updateByPrimaryKey(ScheduleJobLog scheduleJobLog); /** * 批量刪除任務日志信息 * @param logIds */ void deleteBatch(Long[] logIds); }
6.3 QuartzJob.java(quartz定時任務執行類)
package one.stand.task; import com.alibaba.fastjson.JSON; import one.stand.entity.ScheduleJob; import one.stand.entity.ScheduleJobLog; import one.stand.service.task.ScheduleJobLogService; import one.stand.task.util.SpringContextUtils; import one.stand.util.DateUtil; import org.apache.commons.lang.StringUtils; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.quartz.QuartzJobBean; import org.springframework.util.ReflectionUtils; import java.lang.reflect.Method; import java.util.Date; /** * quartz任務執行 * @author zgc * */ public class QuartzJob extends QuartzJobBean { private Logger logger = LoggerFactory.getLogger(getClass()); @Override protected void executeInternal(JobExecutionContext context) throws JobExecutionException { System.out.println("執行quartz任務。。。。。"); String json = context.getMergedJobDataMap().getString("JOB_PARAM_KEY"); //將獲取的對象序列化的json 轉化為實體類對象 ScheduleJob scheduleJob = JSON.parseObject(json, ScheduleJob.class); Long jobId = scheduleJob.getJobId(); String beanName = scheduleJob.getBeanName(); String methodName = scheduleJob.getMethodName(); String params = scheduleJob.getParams(); //quartz沒有被spring管理 所以通過其它方式獲取service ScheduleJobLogService scheduleJobLogService = (ScheduleJobLogService) SpringContextUtils.getBean("scheduleJobLogServiceImpl"); //保存任務記錄日志 ScheduleJobLog scheduleJobLog = new ScheduleJobLog(); scheduleJobLog.setJobId(jobId); scheduleJobLog.setBeanName(beanName); scheduleJobLog.setMethodName(methodName); scheduleJobLog.setParams(params); scheduleJobLog.setCreateTime(new Date()); scheduleJobLog.setBeginTime(DateUtil.getNow()); long startTime = System.currentTimeMillis(); try { Object targetClass = SpringContextUtils.getBean(beanName); Method method = null; //通過反射獲取方法 if (StringUtils.isNotBlank(params)) { method = targetClass.getClass().getDeclaredMethod(methodName, String.class); } else { method = targetClass.getClass().getDeclaredMethod(methodName); } ReflectionUtils.makeAccessible(method);//使方法具有public權限 //根據反射執行方法 if (StringUtils.isNotBlank(params)) { method.invoke(targetClass, params); } else { method.invoke(targetClass); } long endTime = System.currentTimeMillis() - startTime; scheduleJobLog.setEndTime(DateUtil.getNow()); scheduleJobLog.setStatus(0);//保存日志里的操作狀態 0:成功 scheduleJobLog.setTimes(endTime);//耗時多長時間 logger.info("任務執行成功,任務ID:" + jobId + ",總耗時:" + endTime + "毫秒"); } catch (Exception e) { long endTime = System.currentTimeMillis() - startTime; scheduleJobLog.setEndTime(DateUtil.getNow()); scheduleJobLog.setError(StringUtils.substring(e.toString(),2000));//錯誤消息 scheduleJobLog.setStatus(1);//失敗 scheduleJobLog.setTimes(endTime);//耗時 e.printStackTrace(); logger.error("任務執行失敗,任務ID:"+jobId); } finally { //最后調用service保存定時任務日志記錄 scheduleJobLogService.save(scheduleJobLog); } } }
7.quartz任務及任務日志接口實現類
7.2 ScheduleJobServiceImpl.java
package one.stand.service.task.impl; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.quartz.Scheduler; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import com.github.pagehelper.Page; import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import one.stand.entity.ScheduleJob; import one.stand.exception.RRException; import one.stand.mapper.ScheduleJobMapper; import one.stand.model.ResultModel; import one.stand.request.ScheduleJobRequest; import one.stand.service.task.ScheduleJobService; import one.stand.task.Constant; import one.stand.task.util.SchedulerUtils; @SuppressWarnings("unchecked") @Service public class ScheduleJobServiceImpl implements ScheduleJobService{ @Autowired private ScheduleJobMapper scheduleJobMapper; @Autowired private Scheduler scheduler; @Override @Transactional(propagation = Propagation.REQUIRED) public ResultModel<Boolean> save(ScheduleJob scheduleJob) { //保存實體類的信息 scheduleJob.setCreateTime(new Date()); scheduleJob.setStatus(Constant.ScheduleStatus.PAUSE.getValue()); scheduleJobMapper.insertSelective(scheduleJob); //創建定時任務 並保存到對應的quatrz表中 try { SchedulerUtils.createJob(scheduler, scheduleJob); } catch (RRException e) { e.printStackTrace(); return ResultModel.success(false); } return ResultModel.success(true); } @Override public ResultModel<ScheduleJob> queryObject(Long jobId) { return ResultModel.success(scheduleJobMapper.selectByPrimaryKey(jobId)); } @Override public ResultModel<Boolean> update(ScheduleJob scheduleJob) { try { SchedulerUtils.updateJob(scheduler, scheduleJob); } catch (RRException e) { e.printStackTrace(); return ResultModel.success(false); } scheduleJobMapper.updateByPrimaryKeySelective(scheduleJob); return ResultModel.success(true); } @Override public ResultModel<Boolean> deleteBatch(Long[] jobIds) { for(Long jobId : jobIds){ try { SchedulerUtils.deleteJob(scheduler, jobId); } catch (RRException e) { e.printStackTrace(); return ResultModel.success(false); } } //刪除數據 scheduleJobMapper.deleteBatch(jobIds); return ResultModel.success(true); } @Override @Transactional(propagation = Propagation.REQUIRED) public ResultModel<Boolean> pause(Long[] jobIds) { for(Long jobId : jobIds){ try { SchedulerUtils.pauseJob(scheduler, jobId); } catch (RRException e) { e.printStackTrace(); return ResultModel.success(false); } } Map<String, Object> map = new HashMap<>(); map.put("list", jobIds); map.put("status", Constant.ScheduleStatus.PAUSE.getValue()); scheduleJobMapper.updateBatch(map); return ResultModel.success(true); } @Override @Transactional(propagation = Propagation.REQUIRED) public ResultModel<Boolean> resume(Long[] jobIds) { for(Long jobId : jobIds){ try { SchedulerUtils.resumeJob(scheduler, jobId); } catch (RRException e) { e.printStackTrace(); return ResultModel.success(false); } } Map<String, Object> map = new HashMap<>(); map.put("list", jobIds); map.put("status", Constant.ScheduleStatus.NORMAL.getValue()); scheduleJobMapper.updateBatch(map); return ResultModel.success(true); } @Override @Transactional(propagation = Propagation.REQUIRED) public ResultModel<Boolean> run(Long[] jobIds) { for(Long jobId : jobIds){ try { SchedulerUtils.run(scheduler, jobId); } catch (RRException e) { e.printStackTrace(); return ResultModel.success(false); } } return ResultModel.success(true); } @SuppressWarnings({ "rawtypes", "resource" }) @Override public ResultModel<Map<String, Object>> selectList(ScheduleJobRequest request) { ScheduleJob model = new ScheduleJob(); if (request != null) { BeanUtils.copyProperties(request, model); } PageHelper.startPage(request.getPageNum(), request.getPageSize()); List<ScheduleJob> list = scheduleJobMapper.selectList(model); if (list == null || list.size() <= 0) { return ResultModel.noData(); } PageInfo<ScheduleJob> pageInfo = new PageInfo<>(list); Map<String, Object> map = new HashMap<>(1); Page page = (Page) list; pageInfo.setTotal(page.getTotal()); map.put("pageInfo", pageInfo); return ResultModel.success(map); } }
7.2 ScheduleJobLogServiceImpl.java
package one.stand.service.task.impl; import java.util.HashMap; import java.util.List; import java.util.Map; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import com.github.pagehelper.Page; import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import one.stand.entity.ScheduleJobLog; import one.stand.mapper.ScheduleJobLogMapper; import one.stand.request.ScheduleJobLogRequest; import one.stand.service.task.ScheduleJobLogService; @Service public class ScheduleJobLogServiceImpl implements ScheduleJobLogService{ @Autowired private ScheduleJobLogMapper scheduleJobLogMapper; @Override @Transactional(propagation = Propagation.REQUIRED) public void save(ScheduleJobLog scheduleJobLog) { scheduleJobLogMapper.save(scheduleJobLog); } @SuppressWarnings({ "rawtypes", "resource" }) @Override public Map<String, Object> selectList(ScheduleJobLogRequest request) { ScheduleJobLog model = new ScheduleJobLog(); if (request != null) { BeanUtils.copyProperties(request, model); } PageHelper.startPage(request.getPageNum(), request.getPageSize()); List<ScheduleJobLog> list = scheduleJobLogMapper.selectList(model); if (list == null || list.size() <= 0) { return null; } PageInfo<ScheduleJobLog> pageInfo = new PageInfo<>(list); Map<String, Object> map = new HashMap<>(1); Page page = (Page) list; pageInfo.setTotal(page.getTotal()); map.put("pageInfo", pageInfo); return map; } @Override public ScheduleJobLog selectByPrimaryKey(Long logId) { return scheduleJobLogMapper.selectByPrimaryKey(logId); } @Override public void updateByPrimaryKey(ScheduleJobLog scheduleJobLog) { scheduleJobLogMapper.updateByPrimaryKey(scheduleJobLog); } @Override public void deleteBatch(Long[] logIds) { scheduleJobLogMapper.deleteBatch(logIds); } }
8.TaskController(Web接口)
package one.stand.controller.task; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; import com.github.xiaoymin.knife4j.annotations.ApiSort; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiOperation; import one.stand.entity.ScheduleJob; import one.stand.model.ResultModel; import one.stand.request.ScheduleJobLogRequest; import one.stand.request.ScheduleJobRequest; import one.stand.service.task.ScheduleJobLogService; import one.stand.service.task.ScheduleJobService; @Api(value = "task-定時任務",tags = "Auction-定時任務") @ApiSort(3) @RestController @RequestMapping("task") public class TaskController { @Autowired private ScheduleJobService scheduleJobService; @Autowired private ScheduleJobLogService scheduleJobLogService; /** * 查詢定時任務列表 * * @param request * @return */ @ApiOperation("查詢定時任務列表") @ApiOperationSupport(order=1, author="zgc") @ApiImplicitParam(name = "請求參數", value = "request", required = true, dataType="ScheduleJobRequest",paramType="body") @PostMapping("selectList") public ResultModel<Map<String, Object>> selectList(@RequestBody ScheduleJobRequest request) { return scheduleJobService.selectList(request); } /** * 保存定時任務 */ @ApiOperation("保存定時任務") @ApiOperationSupport(order=1, author="zgc") @ApiImplicitParam(name = "請求參數", value = "request", required = true, dataType="ScheduleJob",paramType="body") @PostMapping("save") public ResultModel<Boolean> save(@RequestBody ScheduleJob scheduleJob){ return scheduleJobService.save(scheduleJob); } /** * 定時任務信息 */ @ApiOperation("查詢定時任務信息") @ApiOperationSupport(order=1, author="zgc") @ApiImplicitParam(name = "請求參數", value = "request", required = true, dataType="Long",paramType="query") @GetMapping("info/{jobId}") public ResultModel<ScheduleJob> info(@PathVariable("jobId") Long jobId){ return scheduleJobService.queryObject(jobId); } /** * 修改定時任務 */ @ApiOperation("修改定時任務") @ApiOperationSupport(order=1, author="zgc") @ApiImplicitParam(name = "請求參數", value = "request", required = true, dataType="ScheduleJob",paramType="body") @PostMapping("update") public ResultModel<Boolean> update(@RequestBody ScheduleJob scheduleJob){ return scheduleJobService.update(scheduleJob); } /** * 刪除定時任務 */ @ApiOperation("刪除定時任務") @ApiOperationSupport(order=1, author="zgc") @ApiImplicitParam(name = "請求參數", value = "request", required = true, dataType="Long[]",paramType="body") @PostMapping("del") public ResultModel<Boolean> delete(@RequestBody Long[] jobIds){ return scheduleJobService.deleteBatch(jobIds); } /** * 暫停定時任務 */ @ApiOperation("暫停定時任務") @ApiOperationSupport(order=1, author="zgc") @ApiImplicitParam(name = "請求參數", value = "request", required = true, dataType="Long[]",paramType="body") @PostMapping("pause") public ResultModel<Boolean> pause(@RequestBody Long[] jobIds){ return scheduleJobService.pause(jobIds); } /** * 恢復定時任務 */ @ApiOperation("恢復定時任務") @ApiOperationSupport(order=1, author="zgc") @ApiImplicitParam(name = "請求參數", value = "request", required = true, dataType="Long[]",paramType="body") @PostMapping("resume") public ResultModel<Boolean> resume(@RequestBody Long[] jobIds){ return scheduleJobService.resume(jobIds); } /** * 立即執行定時任務 */ @ApiOperation("立即執行定時任務") @ApiOperationSupport(order=1, author="zgc") @ApiImplicitParam(name = "請求參數", value = "request", required = true, dataType="Long[]",paramType="body") @PostMapping("run") public ResultModel<Boolean> run(@RequestBody Long[] jobIds){ return scheduleJobService.run(jobIds); } /** * 查詢定時任務日志列表 * * @param request * @return */ @SuppressWarnings("unchecked") @ApiOperation("查詢定時任務日志列表") @ApiOperationSupport(order=1, author="zgc") @ApiImplicitParam(name = "請求參數", value = "request", required = true, dataType="ScheduleJobLogRequest",paramType="body") @PostMapping("log/selectList") public ResultModel<Map<String, Object>> logSelectList(@RequestBody ScheduleJobLogRequest request) { return ResultModel.success(scheduleJobLogService.selectList(request)); } }
9.RyTask(測試定時任務類)
package one.stand.service.task; import org.springframework.stereotype.Component; /** * 定時任務調度測試 * */ @Component("ryTask") public class RyTask { public void ryMultipleParams(String s, Boolean b, Long l, Double d, Integer i){ System.out.println("執行多參方法:"); } public void ryParams(String param){ System.out.println("執行有參方法:" + param); } public void ryNoParams(){ System.out.println("執行無參方法"); } }
測試定時任務說明:
(1)beanName為@Component("ryTask")中的ryTask,methodName為對應的方法名,例如:ryParams.實際處理定時任務在上面的定時任務類中處理.
(2)添加定時任務時,如果quartz報錯,請檢查是否是定時任務開始時間或結束時間不可達,即輸入了一個已經過去的時間.
10.SQL文件(schedule_job\schedule_job_log\quartz相關表)
下載鏈接:https://files.cnblogs.com/files/mituxiaogaoyang/quartz.zip