SpringBoot整合quartz實現多個定時任務管理


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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM