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
