Spring Boot -- 定时任务
一:简介
定时任务分为静态定时任务和动态定时任务
- 静态定时任务::依赖于spring-context,静态定时任务将定时时间直接使用注解@Scheduled注解到要定时的方法上,绑定过于耦合,一旦应用启动定时任务就开始了,中间要想更改定时任务(如更改定时时间、更改定时任务相应的逻辑调整等)必须停止服务,手动更改代码重新部署才可,静态定时任务通常用于实现简单的定时任务计划
- 动态定时任务:依赖于spring-boot-starter-quartz,动态定时任务可以通过一套增删改查将要执行的定时代码和定时时间配置到数据库中,这样可以动态的创建定时任务、更新任务、执行任务、暂停任务、继续执行任务、删除任务操作,这些操作是静态定时任务无法实现的,动态定时任务更加灵活
二:静态定时任务
1. pom.xml
静态定时任务会使用到spring-context中的@Scheduled和@EnableScheduling注解,所以不需要特别引入什么依赖,一般很多依赖中都会间接引用到,如spring-boot-starter-web也会间接引用spring-context
2. Component
@Component
public class StaticJob {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final static long SECOND = 1000;
/**
* fixedDelay: 固定延迟时间执行
* 上一次方法执行完成后等待5秒执行
*/
@Scheduled(fixedDelay = 5 * SECOND)
public void fixedDelay(){
logger.info("------------------{}\\tfixedDelay", System.currentTimeMillis());
}
/**
* fixedRate: 固定间隔时间执行
* 每2秒执行一次
* 若在下一次执行时间后方法还没有执行完,则等待上一次方法执行完成后立刻执行本次方法
*/
@Scheduled(fixedRate = 2 * SECOND)
public void fixedRate(){
logger.info("------------------{}\\tfixedRate", System.currentTimeMillis());
}
/**
* cron: 通过 Cron 表达式控制执行
* 每10秒执行一次
* n:执行次数, t:本次方法的执行时间, s:初始执行的时间
* 若t > s + 10,则舍弃n+1的方法执行,执行n+2的方法
*/
@Scheduled(cron = "*/10 * * * * ?")
public void cron(){
logger.info("------------------{}\\tcron", System.currentTimeMillis());
}
}
3. 开启定时任务@EnableScheduling
@EnableScheduling
@SpringBootApplication
public class SpringBootDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootDemoApplication.class, args);
}
}
4.测试
5.@Scheduled注解详解
fixedDelay:上一次方法执行完成后等待n秒在执行方法
/**
* fixedDelay: 固定延迟时间执行
* 上一次方法执行完成后等待5秒执行
*/
@Scheduled(fixedDelay = 5 * SECOND)
public void fixedDelay() throws InterruptedException {
logger.info("------------------{}\\tfixedDelay", System.currentTimeMillis());
Random random = new Random();
int interval = random.nextInt(10) + 1;
logger.info("当前方法执行时间:{}\\秒", interval);
Thread.sleep(1000 * interval);
}
fixedRate:每n秒执行一次,如果当前方法的完成时间超过后面方法的执行时间,则等待当前方法执行完成后立刻执行下一个方法;如果当前方法执行完成后的时间未超过下一个方法的执行时间,则等待下一个方法进行执行;以此类推
/**
* fixedRate: 固定间隔时间执行
* 每4秒执行一次
* 若在下一次执行时间后方法还没有执行完,则等待上一次方法执行完成后立刻执行本次方法
*/
@Scheduled(fixedRate = 4 * SECOND)
public void fixedRate() throws InterruptedException {
logger.info("------------------{}\\tfixedRate", System.currentTimeMillis());
Random random = new Random();
int interval = random.nextInt(10) + 1;
logger.info("当前方法执行时间:{}\\秒", interval);
Thread.sleep(1000 * interval);
}
cron:每n秒执行一次,如果当前方法的完成时间超过后面方法的的执行时间,则舍弃后面已经超过时间的方法,不在执行,执行下一个时间的方法;如果当前方法执行完成后的时间未超过下一个方法的执行时间,则等待下一个方法进行执行;以此类推
/**
* cron: 通过 Cron 表达式控制执行
* 每4秒执行一次
* n:执行次数, t:本次方法的执行时间, s:初始执行的时间
* 若t > s + 10,则舍弃n+1的方法执行,执行n+2的方法
*/
@Scheduled(cron = "*/4 * * * * ?")
public void cron() throws InterruptedException {
logger.info("------------------{}\\tcron", System.currentTimeMillis());
Random random = new Random();
int interval = random.nextInt(10) + 1;
logger.info("当前方法执行时间:{}\\秒", interval);
Thread.sleep(1000 * interval);
}
initialDelay:第一次延迟n秒后执行
@Scheduled(initialDelay = 5 * SECOND, fixedDelay = 2 * SECOND)
public void init(){
logger.info("------------------{}\\init", System.currentTimeMillis());
}
三:动态定时任务
1. pom.xml
动态定时任务是使用的spring-boot-starter-quartz
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
2.sql
/*
Navicat Premium Data Transfer
Source Server : 192.168.1.199
Source Server Type : MySQL
Source Server Version : 50641
Source Host : 192.168.1.199:3306
Source Schema : quartz
Target Server Type : MySQL
Target Server Version : 50641
File Encoding : 65001
Date: 09/09/2018 11:13:23
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for qrtz_blob_triggers
-- ----------------------------
DROP TABLE IF EXISTS `qrtz_blob_triggers`;
CREATE TABLE `qrtz_blob_triggers` (
`SCHED_NAME` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`TRIGGER_NAME` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`TRIGGER_GROUP` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`BLOB_DATA` blob,
PRIMARY KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) USING BTREE,
CONSTRAINT `qrtz_blob_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `qrtz_triggers` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = 'Trigger 作为 Blob 类型存储(用于 Quartz 用户用 JDBC 创建他们自己定制的 Trigger 类型,JobStore 并不知道如何存储实例的时候)' ROW_FORMAT = Compact;
-- ----------------------------
-- Table structure for qrtz_calendars
-- ----------------------------
DROP TABLE IF EXISTS `qrtz_calendars`;
CREATE TABLE `qrtz_calendars` (
`SCHED_NAME` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`CALENDAR_NAME` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`CALENDAR` blob NOT NULL,
PRIMARY KEY (`SCHED_NAME`, `CALENDAR_NAME`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '以 Blob 类型存储 Quartz 的 Calendar 信息 ' ROW_FORMAT = Compact;
-- ----------------------------
-- Table structure for qrtz_cron_triggers
-- ----------------------------
DROP TABLE IF EXISTS `qrtz_cron_triggers`;
CREATE TABLE `qrtz_cron_triggers` (
`SCHED_NAME` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`TRIGGER_NAME` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`TRIGGER_GROUP` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`CRON_EXPRESSION` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`TIME_ZONE_ID` varchar(80) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
PRIMARY KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) USING BTREE,
CONSTRAINT `qrtz_cron_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `qrtz_triggers` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '存储 Cron Trigger,包括 Cron表达式和时区信息 ' ROW_FORMAT = Compact;
-- ----------------------------
-- Table structure for qrtz_fired_triggers
-- ----------------------------
DROP TABLE IF EXISTS `qrtz_fired_triggers`;
CREATE TABLE `qrtz_fired_triggers` (
`SCHED_NAME` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`ENTRY_ID` varchar(95) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`TRIGGER_NAME` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`TRIGGER_GROUP` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`INSTANCE_NAME` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`FIRED_TIME` bigint(13) NOT NULL,
`SCHED_TIME` bigint(13) NOT NULL,
`PRIORITY` int(11) NOT NULL,
`STATE` varchar(16) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`JOB_NAME` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`JOB_GROUP` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`IS_NONCONCURRENT` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`REQUESTS_RECOVERY` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
PRIMARY KEY (`SCHED_NAME`, `ENTRY_ID`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '存储与已触发的 Trigger 相关的状态信息,以及相联 Job 的执行信息' ROW_FORMAT = Compact;
-- ----------------------------
-- Table structure for qrtz_job_details
-- ----------------------------
DROP TABLE IF EXISTS `qrtz_job_details`;
CREATE TABLE `qrtz_job_details` (
`SCHED_NAME` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`JOB_NAME` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`JOB_GROUP` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`DESCRIPTION` varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`JOB_CLASS_NAME` varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`IS_DURABLE` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`IS_NONCONCURRENT` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`IS_UPDATE_DATA` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`REQUESTS_RECOVERY` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`JOB_DATA` blob,
PRIMARY KEY (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '存储每一个已配置的 Job 的详细信息' ROW_FORMAT = Compact;
-- ----------------------------
-- Table structure for qrtz_locks
-- ----------------------------
DROP TABLE IF EXISTS `qrtz_locks`;
CREATE TABLE `qrtz_locks` (
`SCHED_NAME` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`LOCK_NAME` varchar(40) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
PRIMARY KEY (`SCHED_NAME`, `LOCK_NAME`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '存储程序的悲观锁的信息(假如使用了悲观锁)' ROW_FORMAT = Compact;
-- ----------------------------
-- Table structure for qrtz_paused_trigger_grps
-- ----------------------------
DROP TABLE IF EXISTS `qrtz_paused_trigger_grps`;
CREATE TABLE `qrtz_paused_trigger_grps` (
`SCHED_NAME` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`TRIGGER_GROUP` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
PRIMARY KEY (`SCHED_NAME`, `TRIGGER_GROUP`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '存储已暂停的 Trigger 组的信息' ROW_FORMAT = Compact;
-- ----------------------------
-- Table structure for qrtz_scheduler_state
-- ----------------------------
DROP TABLE IF EXISTS `qrtz_scheduler_state`;
CREATE TABLE `qrtz_scheduler_state` (
`SCHED_NAME` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`INSTANCE_NAME` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`LAST_CHECKIN_TIME` bigint(13) NOT NULL,
`CHECKIN_INTERVAL` bigint(13) NOT NULL,
PRIMARY KEY (`SCHED_NAME`, `INSTANCE_NAME`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '存储少量的有关 Scheduler 的状态信息,和别的 Scheduler 实例(假如是用于一个集群中)' ROW_FORMAT = Compact;
-- ----------------------------
-- Table structure for qrtz_simple_triggers
-- ----------------------------
DROP TABLE IF EXISTS `qrtz_simple_triggers`;
CREATE TABLE `qrtz_simple_triggers` (
`SCHED_NAME` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`TRIGGER_NAME` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`TRIGGER_GROUP` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`REPEAT_COUNT` bigint(7) NOT NULL,
`REPEAT_INTERVAL` bigint(12) NOT NULL,
`TIMES_TRIGGERED` bigint(10) NOT NULL,
PRIMARY KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) USING BTREE,
CONSTRAINT `qrtz_simple_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `qrtz_triggers` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '存储简单的 Trigger,包括重复次数,间隔,以及已触的次数' ROW_FORMAT = Compact;
-- ----------------------------
-- Table structure for qrtz_simprop_triggers
-- ----------------------------
DROP TABLE IF EXISTS `qrtz_simprop_triggers`;
CREATE TABLE `qrtz_simprop_triggers` (
`SCHED_NAME` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`TRIGGER_NAME` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`TRIGGER_GROUP` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`STR_PROP_1` varchar(512) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`STR_PROP_2` varchar(512) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`STR_PROP_3` varchar(512) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`INT_PROP_1` int(11) DEFAULT NULL,
`INT_PROP_2` int(11) DEFAULT NULL,
`LONG_PROP_1` bigint(20) DEFAULT NULL,
`LONG_PROP_2` bigint(20) DEFAULT NULL,
`DEC_PROP_1` decimal(13, 4) DEFAULT NULL,
`DEC_PROP_2` decimal(13, 4) DEFAULT NULL,
`BOOL_PROP_1` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`BOOL_PROP_2` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
PRIMARY KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) USING BTREE,
CONSTRAINT `qrtz_simprop_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `qrtz_triggers` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
-- ----------------------------
-- Table structure for qrtz_triggers
-- ----------------------------
DROP TABLE IF EXISTS `qrtz_triggers`;
CREATE TABLE `qrtz_triggers` (
`SCHED_NAME` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`TRIGGER_NAME` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`TRIGGER_GROUP` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`JOB_NAME` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`JOB_GROUP` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`DESCRIPTION` varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`NEXT_FIRE_TIME` bigint(13) DEFAULT NULL,
`PREV_FIRE_TIME` bigint(13) DEFAULT NULL,
`PRIORITY` int(11) DEFAULT NULL,
`TRIGGER_STATE` varchar(16) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`TRIGGER_TYPE` varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`START_TIME` bigint(13) NOT NULL,
`END_TIME` bigint(13) DEFAULT NULL,
`CALENDAR_NAME` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`MISFIRE_INSTR` smallint(2) DEFAULT NULL,
`JOB_DATA` blob,
PRIMARY KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) USING BTREE,
INDEX `SCHED_NAME`(`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`) USING BTREE,
CONSTRAINT `qrtz_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`) REFERENCES `qrtz_job_details` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '存储已配置的 Trigger 的信息' ROW_FORMAT = Compact;
-- ----------------------------
-- Table structure for schedule_job
-- ----------------------------
DROP TABLE IF EXISTS `schedule_job`;
CREATE TABLE `schedule_job` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`class_name` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`cron_expression` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`job_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`job_group` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`trigger_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`trigger_group` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`pause` tinyint(1) DEFAULT 0,
`enable` tinyint(1) DEFAULT 1,
`description` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`create_time` timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
`last_update_time` timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(0),
PRIMARY KEY (`id`) USING BTREE,
INDEX `i_schedule_job_id`(`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '自定义管理定时任务' ROW_FORMAT = Compact;
SET FOREIGN_KEY_CHECKS = 1;
3.domain
/**
* Created by XB on 2018/9/5.
* 自定义管理定时任务
*/
public class ScheduleJob implements Serializable {
private Long id;
private String className;
private String cronExpression;
private String jobName;
private String jobGroup;
private String triggerName;
private String triggerGroup;
private Boolean pause;
private Boolean enable;
private String description;
private Date createTime;
private Date lastUpdateTime;
//get/set
}
4.dao
@Repository
public interface JobDao {
public ScheduleJob select(Long id);
public Integer update(ScheduleJob scheduleJob);
public Integer insert(ScheduleJob scheduleJob);
public Integer delete(Long productId);
public List<ScheduleJob> getAllJob();
public List<ScheduleJob> getAllEnableJob();
}
5.mapper
<resultMap id="scheduleJobMap" type="scheduleJob">
<id property="id" column="id"/>
<id property="id" column="id"/>
<result property="className" column="class_name"/>
<result property="cronExpression" column="cron_expression"/>
<result property="jobName" column="job_name"/>
<result property="jobGroup" column="job_group"/>
<result property="triggerName" column="trigger_name"/>
<result property="triggerGroup" column="trigger_group"/>
<result property="pause" column="pause"/>
<result property="enable" column="enable"/>
<result property="description" column="description"/>
<result property="createTime" column="create_time"/>
<result property="lastUpdateTime" column="last_update_time"/>
</resultMap>
<select id="select" parameterType="Long" resultMap="scheduleJobMap">
<![CDATA[
select id, class_name, cron_expression,
job_name, job_group, trigger_name, trigger_group,
pause, enable, description, create_time, last_update_time
from schedule_job
where id = #{id}
]]>
</select>
<select id="getAllJob" resultMap="scheduleJobMap">
<![CDATA[
select id, class_name, cron_expression,
job_name, job_group, trigger_name, trigger_group,
pause, enable, description, create_time, last_update_time
from schedule_job
]]>
</select>
<select id="getAllEnableJob" resultMap="scheduleJobMap">
<![CDATA[
select id, class_name, cron_expression,
job_name, job_group, trigger_name, trigger_group,
pause, enable, description, create_time, last_update_time
from schedule_job
where enable = 1
]]>
</select>
<update id="update" parameterType="scheduleJob">
<![CDATA[
update schedule_job
]]>
<set>
<if test="className != null">
class_name = #{className},
</if>
<if test="cronExpression != null">
cron_expression = #{cronExpression},
</if>
<if test="jobName != null">
job_name = #{jobName},
</if>
<if test="jobGroup != null">
job_group = #{jobGroup},
</if>
<if test="triggerName != null">
trigger_name = #{triggerName},
</if>
<if test="triggerGroup != null">
trigger_group = #{triggerGroup},
</if>
<if test="pause != null">
pause = #{pause},
</if>
<if test="enable != null">
enable = #{enable},
</if>
<if test="description != null">
description = #{description},
</if>
</set>
<![CDATA[
where id = #{id}
]]>
</update>
<insert id="insert" parameterType="scheduleJob">
<![CDATA[
insert into schedule_job
]]>
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="className != null">
class_name,
</if>
<if test="cronExpression != null ">
cron_expression,
</if>
<if test="jobName != null ">
job_name,
</if>
<if test="jobGroup != null ">
job_group,
</if>
<if test="triggerName != null ">
trigger_name,
</if>
<if test="triggerGroup != null ">
trigger_group,
</if>
<if test="pause != null ">
pause,
</if>
<if test="enable != null ">
enable,
</if>
<if test="description != null ">
description,
</if>
</trim>
<trim prefix=" VALUES (" suffix=")" suffixOverrides=",">
<if test="className != null ">
#{className},
</if>
<if test="cronExpression != null ">
#{cronExpression},
</if>
<if test="jobName != null ">
#{jobName},
</if>
<if test="jobGroup != null ">
#{jobGroup},
</if>
<if test="triggerName != null ">
#{triggerName},
</if>
<if test="triggerGroup != null ">
#{triggerGroup},
</if>
<if test="pause != null ">
#{pause},
</if>
<if test="enable != null ">
#{enable},
</if>
<if test="description != null ">
#{description},
</if>
</trim>
</insert>
<delete id="delete" parameterType="Long">
<![CDATA[
update schedule_job set enable = false where id = #{id}
]]>
</delete>
6. exception
public class ServiceException extends Exception {
public ServiceException(String msg, Exception e){
super(msg, e);
}
public ServiceException(String msg){
super(msg);
}
}
7. service
@Service("jobService")
public class JobService {
@Resource
private JobDao jobDao;
//任务调度器
@Resource
private Scheduler scheduler;
private final Logger logger = LoggerFactory.getLogger(getClass());
public List<ScheduleJob> getAllJob(){
return jobDao.getAllJob();
}
public List<ScheduleJob> getAllEnableJob(){
return jobDao.getAllEnableJob();
}
public ScheduleJob select(Long id) throws ServiceException {
ScheduleJob scheduleJob = jobDao.select(id);
if (scheduleJob == null){
throw new ServiceException("ScheduleJob:" + id + " not found");
}
return scheduleJob;
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = DataAccessException.class)
public ScheduleJob update(ScheduleJob scheduleJob) throws ServiceException {
if(jobDao.update(scheduleJob) <= 0){
throw new ServiceException("Update product failed");
}
ScheduleUtil.updateScheduleJob(scheduler, scheduleJob);
return scheduleJob;
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = DataAccessException.class)
public boolean add(ScheduleJob scheduleJob) throws ServiceException {
if(jobDao.insert(scheduleJob) <= 0){
throw new ServiceException("Add product failed");
}
ScheduleUtil.createScheduleJob(scheduler, scheduleJob);
return true;
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = DataAccessException.class)
public boolean delete(Long id) throws ServiceException {
ScheduleJob scheduleJob = select(id);
if(jobDao.delete(id) <= 0){
throw new ServiceException("Delete product failed");
}
ScheduleUtil.deleteJob(scheduler, scheduleJob);
return true;
}
public boolean run(Long id) throws ServiceException {
ScheduleJob scheduleJob = updateScheduleJobStatus(id, false);
ScheduleUtil.run(scheduler, scheduleJob);
return true;
}
public boolean pause(Long id) throws ServiceException {
ScheduleJob scheduleJob = updateScheduleJobStatus(id, true);
ScheduleUtil.pauseJob(scheduler, scheduleJob);
return true;
}
public boolean resume(Long id) throws ServiceException {
ScheduleJob scheduleJob = updateScheduleJobStatus(id, false);
ScheduleUtil.resumeJob(scheduler, scheduleJob);
return true;
}
private ScheduleJob updateScheduleJobStatus(Long id, boolean isPause) throws ServiceException {
ScheduleJob scheduleJob = select(id);
scheduleJob.setPause(isPause);
update(scheduleJob);
return scheduleJob;
}
}
8. controller
@RestController
@RequestMapping("/job")
public class JobController {
@Resource
private JobService jobService;
@GetMapping
public Object getAllJob() {
return jobService.getAllJob();
}
@GetMapping("/{id}")
public Object getJob(@PathVariable("id") Long id) throws ServiceException {
return jobService.select(id);
}
@PutMapping("/update")
public Object updateJob(@RequestBody ScheduleJob scheduleJob) throws ServiceException {
return jobService.update(scheduleJob);
}
@DeleteMapping("/delete/{id}")
public Object deleteJob(@PathVariable("id") Long id) throws ServiceException {
return jobService.delete(id);
}
@PostMapping("/save")
public Object saveJob(@RequestBody ScheduleJob scheduleJob) throws ServiceException {
return jobService.add(scheduleJob);
}
@GetMapping("/run/{id}")
public Object runJob(@PathVariable("id") Long id) throws ServiceException {
return jobService.run(id);
}
@GetMapping("/pause/{id}")
public Object pauseJob(@PathVariable("id") Long id) throws ServiceException {
return jobService.pause(id);
}
@GetMapping("/resume/{id}")
public Object resumeJob(@PathVariable("id") Long id) throws ServiceException {
return jobService.resume(id);
}
}
9. ScheduleUtil
封装了创建定时任务、更新任务、执行任务、暂停任务、继续执行任务、删除任务等对任务的各种操作
public class ScheduleUtil {
private final static Logger logger = LoggerFactory.getLogger(ScheduleUtil.class);
/**
* 通过触发器用户信息获取 Trigger Key
* @param scheduleJob
* @return
*/
public static TriggerKey getTriggerKey(ScheduleJob scheduleJob){
return TriggerKey.triggerKey(scheduleJob.getTriggerName(), scheduleJob.getTriggerGroup());
}
/**
* 通过JobDetail用户信息获取 Job Key
* @param scheduleJob
* @return
*/
public static JobKey getJobKey(ScheduleJob scheduleJob){
return JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
}
/**
* 获取 Cron Trigger
* @param scheduler
* @param scheduleJob
* @return
* @throws ServiceException
*/
public static CronTrigger getCronTrigger(Scheduler scheduler, ScheduleJob scheduleJob) throws ServiceException {
try {
return (CronTrigger) scheduler.getTrigger(getTriggerKey(scheduleJob));
} catch (SchedulerException e) {
throw new ServiceException("Get Cron trigger failed", e);
}
}
/**
* 创建任务
* @param scheduler
* @param scheduleJob
*/
public static void createScheduleJob(Scheduler scheduler, ScheduleJob scheduleJob) throws ServiceException {
try {
//要执行的 Job 的类
Class<? extends Job> jobClass = (Class<? extends Job>) Class.forName(scheduleJob.getClassName()).newInstance().getClass();
//JobDetail:用来描述Job实现类及其它相关的静态信息
JobDetail jobDetail = JobBuilder.newJob(jobClass)
.withIdentity(scheduleJob.getJobName(), scheduleJob.getJobGroup())//设置job的用户和用户组
.withDescription(scheduleJob.getDescription())//描述
.build();
//定义cron规则
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression())
.withMisfireHandlingInstructionDoNothing();
//定义触发器
CronTrigger cronTrigger = TriggerBuilder.newTrigger()
.withIdentity(scheduleJob.getTriggerName(), scheduleJob.getTriggerGroup())//设置trigger的用户和用户组
.withDescription(scheduleJob.getDescription())
.withSchedule(scheduleBuilder)//设置触发器规则
.startNow().build();//一旦加入scheduler,立即生效
//将任务描述和触发器加入scheduler
scheduler.scheduleJob(jobDetail, cronTrigger);
logger.info("Create schedule job {}-{} success", scheduleJob.getJobGroup(), scheduleJob.getJobName());
if(scheduleJob.getPause()){
pauseJob(scheduler, scheduleJob);
}
} catch (Exception e) {
e.printStackTrace();
logger.error("Execute schedule job failed");
throw new ServiceException("Execute schedule job failed", e);
}
}
/**
* 更新任务(触发规则)
* @param scheduler
* @param scheduleJob
*/
public static void updateScheduleJob(Scheduler scheduler, ScheduleJob scheduleJob) throws ServiceException {
try {
//获取触发器key
TriggerKey triggerKey = getTriggerKey(scheduleJob);
//定义更新后的cron规则
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression())
.withMisfireHandlingInstructionDoNothing();
//获取以前的触发器
CronTrigger cronTrigger = getCronTrigger(scheduler, scheduleJob);
cronTrigger = cronTrigger.getTriggerBuilder()
.withIdentity(triggerKey)
.withDescription(scheduleJob.getDescription())
.withSchedule(cronScheduleBuilder).build();
scheduler.rescheduleJob(triggerKey, cronTrigger);
logger.info("Update schedule job {}-{} success", scheduleJob.getJobGroup(), scheduleJob.getJobName());
if(scheduleJob.getPause()){
pauseJob(scheduler, scheduleJob);
}
} catch (SchedulerException e) {
e.printStackTrace();
logger.error("Update schedule job failed");
throw new ServiceException("Update schedule job failed", e);
}
}
/**
* 执行任务
* @param scheduler
* @param scheduleJob
*/
public static void run(Scheduler scheduler, ScheduleJob scheduleJob) throws ServiceException {
try {
scheduler.triggerJob(getJobKey(scheduleJob));
logger.info("Run schedule job {}-{} success", scheduleJob.getJobGroup(), scheduleJob.getJobName());
} catch (SchedulerException e) {
e.printStackTrace();
logger.error("Run schedule job failed");
throw new ServiceException("Run schedule job failed", e);
}
}
/**
* 暂停任务
* @param scheduler
* @param scheduleJob
*/
public static void pauseJob(Scheduler scheduler, ScheduleJob scheduleJob) throws ServiceException {
try {
scheduler.pauseJob(getJobKey(scheduleJob));
logger.info("Pause schedule job {}-{} success", scheduleJob.getJobGroup(), scheduleJob.getJobName());
} catch (SchedulerException e) {
e.printStackTrace();
logger.error("Pause schedule job failed");
throw new ServiceException("Pause job failed", e);
}
}
/**
* 继续执行任务
* @param scheduler
* @param scheduleJob
* @throws ServiceException
*/
public static void resumeJob(Scheduler scheduler, ScheduleJob scheduleJob) throws ServiceException {
try {
scheduler.resumeJob(getJobKey(scheduleJob));
logger.info("Resume schedule job {}-{} success", scheduleJob.getJobGroup(), scheduleJob.getJobName());
} catch (SchedulerException e) {
e.printStackTrace();
logger.error("Resume schedule job failed");
throw new ServiceException("Resume job failed", e);
}
}
/**
* 删除任务
* @param scheduler
* @param scheduleJob
* @throws ServiceException
*/
public static void deleteJob(Scheduler scheduler, ScheduleJob scheduleJob) throws ServiceException {
try {
scheduler.deleteJob(getJobKey(scheduleJob));
logger.info("Delete schedule job {}-{} success", scheduleJob.getJobGroup(), scheduleJob.getJobName());
} catch (SchedulerException e) {
e.printStackTrace();
logger.error("Delete schedule job failed");
throw new ServiceException("Delete job failed", e);
}
}
}
10. listener
程序启动时,执行所有可用的任务
@Component
public class ApplicationListener implements CommandLineRunner {
private final Logger logger = LoggerFactory.getLogger(getClass());
@Resource
private JobService jobService;
@Resource
private Scheduler scheduler;
@Override
public void run(String... args) throws Exception {
//应用启动之后执行所有可执行的的任务
List<ScheduleJob> scheduleJobList = jobService.getAllEnableJob();
for (ScheduleJob scheduleJob : scheduleJobList){
try {
CronTrigger cronTrigger = ScheduleUtil.getCronTrigger(scheduler, scheduleJob);
if (cronTrigger == null) {
ScheduleUtil.createScheduleJob(scheduler, scheduleJob);
} else {
ScheduleUtil.updateScheduleJob(scheduler, scheduleJob);
}
logger.info("Startup {}-{} success", scheduleJob.getJobGroup(), scheduleJob.getJobName());
} catch (ServiceException e) {
e.printStackTrace();
}
}
}
}
11. job
Job 用于封装要定时执行的任务
public class TestJob implements Job {
private final Logger logger = LoggerFactory.getLogger(getClass());
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
logger.info("Test job is executing..." + jobExecutionContext.getJobDetail().getDescription());
}
}
12. 测试
通过Postman发送一个http请求到JobController进行测试相关操作