本文主要详细介绍了SpringBoot架构下搭配Quartz动态定时任务的使用,定时任务表达式配置在数据库中。
支持查看任务状态,动态修改任务时间,停止任务等。启动类启动后任务自启动,一个字,方便!下面我们来看看如何实现:

按我的步骤一步一步来即可,先在springboot项目建一个quartz模块,配置好配置文件(服务名等,可参照你其他模块的配置文件),然后执行以下步骤

1.前置准备
1.1数据库创建任务表
CREATE TABLE `scheduled_task` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`task_key` varchar(128) NOT NULL COMMENT '任务key值(使用bean名称)',
`task_desc` varchar(128) DEFAULT NULL COMMENT '任务描述',
`task_cron` varchar(128) NOT NULL COMMENT '任务表达式',
`init_start_flag` int(2) NOT NULL DEFAULT '1' COMMENT '程序初始化是否启动 1 是 0 否',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uniqu_task_key` (`task_key`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

1.2添加初始化测试数据
INSERT INTO `cron`.`scheduled_task`(`id`, `task_key`, `task_desc`, `task_cron`, `init_start_flag`, `create_time`, `update_time`) VALUES (1, 'scheduledTask01', '定时任务01', '0/1 * * * * ?', 1, NOW(), NOW());
INSERT INTO `cron`.`scheduled_task`(`id`, `task_key`, `task_desc`, `task_cron`, `init_start_flag`, `create_time`, `update_time`) VALUES (2, 'scheduledTask02', '定时任务02', '0/1 * * * * ?', 0, NOW(), NOW());
INSERT INTO `cron`.`scheduled_task`(`id`, `task_key`, `task_desc`, `task_cron`, `init_start_flag`, `create_time`, `update_time`) VALUES (3, 'scheduledTask03', '定时任务03', '0/1 * * * * ?', 1, NOW(), NOW());

我这里corn配置的是每分钟执行一次,目的:方便自测

1.3创建Maven项目并添加Maven依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.12.RELEASE</version>
</parent>

<!--版本号 -->
<properties>
<fastjson.version>1.2.47</fastjson.version>
<mybatis-spring-boot.version>1.3.1</mybatis-spring-boot.version>
<mybatis-spring-boot-starter.version>1.3.0</mybatis-spring-boot-starter.version>
<mybatis-plus.version>2.1-gamma</mybatis-plus.version>
<druid.version>1.1.2</druid.version>
</properties>
<dependencies>
<!--依赖管理 -->
<dependency><!--添加Web依赖 -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency><!--添加Mybatis依赖 -->
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-spring-boot.version}</version>
</dependency>
<dependency><!--添加MySql依赖 -->
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency><!--添加Test依赖 -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- json 工具 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
</dependencies>

1.4添加配置文件application.properties
server.port=8081
spring.datasource.url:jdbc:mysql://localhost:3306/cron?useSSL=false
spring.datasource.username:root
spring.datasource.password:root

1.5添加SpringBoot启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

2.任务调度
2.1创建定时任务线程池,初始化任务Map
       注:任务map,存的是你的任务信息,项目启动即把这些任务初始化

import com.scheduled.dynamic.enums.ScheduledTaskEnum;
import com.scheduled.dynamic.service.ScheduledTaskJob;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import java.util.Map;
@Configuration
public class ScheduledTaskConfig {

/**
* 日志
*/
private static final Logger LOGGER = LoggerFactory.getLogger(ScheduledTaskConfig.class);

@Bean
public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
LOGGER.info("创建定时任务调度线程池 start");
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(20);
threadPoolTaskScheduler.setThreadNamePrefix("taskExecutor-");
threadPoolTaskScheduler.setWaitForTasksToCompleteOnShutdown(true);
threadPoolTaskScheduler.setAwaitTerminationSeconds(60);
LOGGER.info("创建定时任务调度线程池 end");
return threadPoolTaskScheduler;
}

/**
* 初始化定时任务Map
* key :任务key
* value : 执行接口实现
*/
@Bean(name = "scheduledTaskJobMap")
public Map<String, ScheduledTaskJob> scheduledTaskJobMap() {
return ScheduledTaskEnum.initScheduledTask();
}

}

2.2创建任务实体Bean
public class ScheduledTaskBean {

/**
* 任务key值 唯一
*/
private String taskKey;
/**
* 任务描述
*/
private String taskDesc;
/**
* 任务表达式
*/
private String taskCron;

/**
* 程序初始化是否启动 1 是 0 否
*/
private Integer initStartFlag;

/**
* 当前是否已启动
*/
private boolean startFlag;
//省略 get/set  get/set你可以加@Data就行了

2.3Mapper接口
/**
* 定时任务表 mapper
* 这里我就用注解写了,偷偷懒
*/
@Mapper
public interface ScheduledTaskMapper {

/**
* 根据key 获取 任务信息
*/
@Select("select task_key as taskKey,task_desc as taskDesc,task_cron as taskCron,init_start_flag as initStartFlag from scheduled_task where task_key = '${taskKey}' ")
ScheduledTaskBean getByKey(@Param("taskKey") String taskKey);

/**
* 获取程序初始化需要自启的任务信息
*/
@Select("select task_key as taskKey,task_desc as taskDesc,task_cron as taskCron,init_start_flag as initStartFlag from scheduled_task where init_start_flag=1 ")
List<ScheduledTaskBean> getAllNeedStartTask();

/**
* 获取所有任务
*/
@Select("select task_key as taskKey,task_desc as taskDesc,task_cron as taskCron,init_start_flag as initStartFlag from scheduled_task ")
List<ScheduledTaskBean> getAllTask();
}

2.4创建调度任务公共父接口
/**
* 调度任务公共父接口
*/
public interface ScheduledTaskJob extends Runnable {
}

2.5实现父接口,重写run方法
注:ScheduledTask02和ScheduledTask03,我没写了,你复制01复制一下,把名字改了就行

import com.scheduled.dynamic.service.ScheduledTaskJob;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 任务 01
*/
public class ScheduledTask01 implements ScheduledTaskJob {
/**
* 日志
*/
private static final Logger LOGGER = LoggerFactory.getLogger(ScheduledTask01.class);

@Override
public void run() {
LOGGER.info("ScheduledTask => 01 run 当前线程名称 {} ", Thread.currentThread().getName());
}
}

2.6创建定时任务枚举类
注:方便管理任务

import com.scheduled.dynamic.service.ScheduledTaskJob;
import com.scheduled.dynamic.task.*;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
* 定时任务枚举值
* 注:key 需要与数据库保持一致!!!(非常重要,不然报错让你怀疑人生)
*/
public enum ScheduledTaskEnum {

/**
* 任务1
*/
TASK_01("scheduledTask01", new ScheduledTask01()),
/**
* 任务2
*/
TASK_02("scheduledTask02", new ScheduledTask02()),
/**
* 任务3
*/
TASK_03("scheduledTask03", new ScheduledTask03()),
;
/**
* 定时任务key
*/
private String taskKey;
/**
* 定时任务 执行实现类
*/
private ScheduledTaskJob scheduledTaskJob;

ScheduledTaskEnum(String taskKey, ScheduledTaskJob scheduledTaskJob) {
this.taskKey = taskKey;
this.scheduledTaskJob = scheduledTaskJob;
}

/**
* 初始化 所有任务
*/
public static Map<String, ScheduledTaskJob> initScheduledTask() {
if (ScheduledTaskEnum.values().length < 0) {
return new ConcurrentHashMap<>();
}
Map<String, ScheduledTaskJob> scheduledTaskJobMap = new ConcurrentHashMap<>();
for (ScheduledTaskEnum taskEnum : ScheduledTaskEnum.values()) {
scheduledTaskJobMap.put(taskEnum.taskKey, taskEnum.scheduledTaskJob);
}
return scheduledTaskJobMap;
}
}

2.7功能核心接口
/**
* 定时任务接口
*/
public interface ScheduledTaskService {

/**
* 所有任务列表
*/
List<ScheduledTaskBean> taskList();

/**
* 根据任务key 启动任务
*/
Boolean start(String taskKey);

/**
* 根据任务key 停止任务
*/
Boolean stop(String taskKey);

/**
* 根据任务key 重启任务
*/
Boolean restart(String taskKey);

/**
* 程序启动时初始化 ==> 启动所有正常状态的任务
*/
void initAllTask(List<ScheduledTaskBean> scheduledTaskBeanList);

}

/**
* 定时任务实现
*/
@Service
public class ScheduledTaskServiceImpl implements ScheduledTaskService {
/**
* 日志
*/
private static final Logger LOGGER = LoggerFactory.getLogger(ScheduledTaskServiceImpl.class);

@Autowired
private ScheduledTaskMapper taskMapper;
/**
* 可重入锁
*/
private ReentrantLock lock = new ReentrantLock();
/**
* 定时任务线程池
*/
@Autowired
private ThreadPoolTaskScheduler threadPoolTaskScheduler;
/**
* 所有定时任务存放Map
* key :任务 key
* value:任务实现
*/
@Autowired
@Qualifier(value = "scheduledTaskJobMap")
private Map<String, ScheduledTaskJob> scheduledTaskJobMap;

/**
* 存放已经启动的任务map
*/
private Map<String, ScheduledFuture> scheduledFutureMap = new ConcurrentHashMap<>();

/**
* 所有任务列表
*/
@Override
public List<ScheduledTaskBean> taskList() {
LOGGER.info(">>>>>> 获取任务列表开始 >>>>>> ");
//数据库查询所有任务 => 未做分页
List<ScheduledTaskBean> taskBeanList = taskMapper.getAllTask();
if (CollectionUtils.isEmpty(taskBeanList)) {
return new ArrayList<>();
}

for (ScheduledTaskBean taskBean : taskBeanList) {
String taskKey = taskBean.getTaskKey();
//是否启动标记处理
taskBean.setStartFlag(this.isStart(taskKey));
}
LOGGER.info(">>>>>> 获取任务列表结束 >>>>>> ");
return taskBeanList;
}

/**
* 根据任务key 启动任务
*/
@Override
public Boolean start(String taskKey) {
LOGGER.info(">>>>>> 启动任务 {} 开始 >>>>>>", taskKey);
//添加锁放一个线程启动,防止多人启动多次
lock.lock();
LOGGER.info(">>>>>> 添加任务启动锁完毕");
try {
//校验是否已经启动
if (this.isStart(taskKey)) {
LOGGER.info(">>>>>> 当前任务已经启动,无需重复启动!");
return false;
}
//校验任务是否存在
if (!scheduledTaskJobMap.containsKey(taskKey)) {
return false;
}
//根据key数据库获取任务配置信息
ScheduledTaskBean scheduledTask = taskMapper.getByKey(taskKey);
//启动任务
this.doStartTask(scheduledTask);
} finally {
// 释放锁
lock.unlock();
LOGGER.info(">>>>>> 释放任务启动锁完毕");
}
LOGGER.info(">>>>>> 启动任务 {} 结束 >>>>>>", taskKey);
return true;
}

/**
* 根据 key 停止任务
*/
@Override
public Boolean stop(String taskKey) {
LOGGER.info(">>>>>> 进入停止任务 {} >>>>>>", taskKey);
//当前任务实例是否存在
boolean taskStartFlag = scheduledFutureMap.containsKey(taskKey);
LOGGER.info(">>>>>> 当前任务实例是否存在 {}", taskStartFlag);
if (taskStartFlag) {
//获取任务实例
ScheduledFuture scheduledFuture = scheduledFutureMap.get(taskKey);
//关闭实例
scheduledFuture.cancel(true);
}
LOGGER.info(">>>>>> 结束停止任务 {} >>>>>>", taskKey);
return taskStartFlag;
}

/**
* 根据任务key 重启任务
*/
@Override
public Boolean restart(String taskKey) {
LOGGER.info(">>>>>> 进入重启任务 {} >>>>>>", taskKey);
//先停止
this.stop(taskKey);
//再启动
return this.start(taskKey);
}

/**
* 程序启动时初始化 ==> 启动所有正常状态的任务
*/
@Override
public void initAllTask(List<ScheduledTaskBean> scheduledTaskBeanList) {
LOGGER.info("程序启动 ==> 初始化所有任务开始 !size={}", scheduledTaskBeanList.size());
if (CollectionUtils.isEmpty(scheduledTaskBeanList)) {
return;
}
for (ScheduledTaskBean scheduledTask : scheduledTaskBeanList) {
//任务 key
String taskKey = scheduledTask.getTaskKey();
//校验是否已经启动
if (this.isStart(taskKey)) {
continue;
}
//启动任务
this.doStartTask(scheduledTask);
}
LOGGER.info("程序启动 ==> 初始化所有任务结束 !size={}", scheduledTaskBeanList.size());
}

/**
* 执行启动任务
*/
private void doStartTask(ScheduledTaskBean scheduledTask) {
//任务key
String taskKey = scheduledTask.getTaskKey();
//定时表达式
String taskCron = scheduledTask.getTaskCron();
//获取需要定时调度的接口
ScheduledTaskJob scheduledTaskJob = scheduledTaskJobMap.get(taskKey);
LOGGER.info(">>>>>> 任务 [ {} ] ,cron={}", scheduledTask.getTaskDesc(), taskCron);
ScheduledFuture scheduledFuture = threadPoolTaskScheduler.schedule(scheduledTaskJob,
new Trigger() {
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
CronTrigger cronTrigger = new CronTrigger(taskCron);
return cronTrigger.nextExecutionTime(triggerContext);
}
});
//将启动的任务放入 map
scheduledFutureMap.put(taskKey, scheduledFuture);
}

/**
* 任务是否已经启动
*/
private Boolean isStart(String taskKey) {
//校验是否已经启动
if (scheduledFutureMap.containsKey(taskKey)) {
if (!scheduledFutureMap.get(taskKey).isCancelled()) {
return true;
}
}
return false;
}

}

2.8 定时任务Controller

/**
* 定时任务 controller
*/
@RestController
@RequestMapping("/scheduled")
public class ScheduledController {
@Autowired
private ScheduledTaskService scheduledTaskService;
/**
* 所有任务列表
*/
@RequestMapping("/taskList")
public List<ScheduledTaskBean> taskList() {
return scheduledTaskService.taskList();
}

/**
* 根据任务key => 启动任务
*/
@RequestMapping("/start")
public String start(@RequestParam("taskKey") String taskKey) {
scheduledTaskService.start(taskKey);
return "start success";
}

/**
* 根据任务key => 停止任务
*/
@RequestMapping("/stop")
public String stop(@RequestParam("taskKey") String taskKey) {
scheduledTaskService.stop(taskKey);
return "stop success";
}

/**
* 根据任务key => 重启任务
*/
@RequestMapping("/restart")
public String restart(@RequestParam("taskKey") String taskKey) {
scheduledTaskService.restart(taskKey);
return "restart success";
}

}

到这里,恭喜你,CV大法又精进了,咳咳,偏了偏了,是已经可以实现,动态管理任务了,包括动态修改启动表达式,停止任务,启动任务,重启任务等。

但是,还有一个问题,项目重启后,需要人工来启动需要启动的任务。谁不想偷点懒呢,让它自启动多好,打包扔上去启动就行了,谁还一个一个去启,麻烦,于是我们再加一步:

2.9实现项目启动时自启动
注:SpringBoot给我们提供了两个接口来帮助我们实现这种需求。这两个接口分别为CommandLineRunner和ApplicationRunner。他们的执行时机为容器启动完成的时候。

/**
* @see @Order注解的执行优先级是按value值从小到大顺序。
* 项目启动完毕后开启需要自启的任务
*/
@Component
@Order(value = 1)
public class ScheduledTaskRunner implements ApplicationRunner {
/**
* 日志
*/
private static final Logger LOGGER = LoggerFactory.getLogger(ScheduledTaskRunner.class);

@Autowired
private ScheduledTaskMapper taskMapper;

@Autowired
private ScheduledTaskService scheduledTaskService;

/**
* 程序启动完毕后,需要自启的任务
*/
@Override
public void run(ApplicationArguments applicationArguments) throws Exception {
LOGGER.info(" >>>>>> 项目启动完毕, 开启 => 需要自启的任务 开始!");
List<ScheduledTaskBean> scheduledTaskBeanList = taskMapper.getAllNeedStartTask();
scheduledTaskService.initAllTask(scheduledTaskBeanList);
LOGGER.info(" >>>>>> 项目启动完毕, 开启 => 需要自启的任务 结束!");
}
}

到这里,算是没啥问题了,只需要把你的任务模块代码写到run方法里就行

福利:
https://github.com/mengq0815/spring-boot-example/tree/master/spring-boot-scheduled-task
---------------------
作者:君子博学而日参省乎己
来源:博客园

转载于:https://www.cnblogs.com/yj321/p/11214148.html

SpringBoot+Quartz实现动态可配定时任务(动态定时任务)相关推荐

  1. SpringBoot+Quartz动态管理定时任务

    前置理论: 1.小顶堆(适合任务少的,因为向下调整耗费性能) 堆:是一完全二叉树(除了最后一层节点其他层都达到最大节点数,且最后一层都靠左排列):堆中某个节点的值总不大于或不小于其父节点. 定时任务是 ...

  2. 定时任务-动态定时任务(springboot+org.quartz)

    添加依赖 <!-- Quartz定时任务 --><dependency><groupId>org.springframework.boot</groupId& ...

  3. 定时任务动态管理-Scheduled

    文章目录 前言 一.架构流程图 二.代码实现流程 1.引入库 2.代码流程 前言 定时任务动态管理分为两种方式: 方式一:Web前台配置Trigger触发器(关联Cron).ThreadPoolTas ...

  4. jeecg 3.7.1 新版功能,集群定时任务动态发布模块 使用规则

    jeecg 3.7.1  集群定时任务动态发布模块 使用规则 新版特性:    支持集群定时任务,支持分布式. 菜单路径: 系统监控-->定时任务 字段说明: 任务ID.任务说明:自定义即可 c ...

  5. 第四十章:基于SpringBoot Quartz完成定时任务分布式多节点负载持久化

    在上一章[第三十九章:基于SpringBoot & Quartz完成定时任务分布式单节点持久化]中我们已经完成了任务的持久化,当我们创建一个任务时任务会被quartz定时任务框架自动持久化到数 ...

  6. SpringBoot多数据源切换,AOP实现动态数据源切换

    SpringBoot多数据源切换,AOP实现动态数据源切换 操作数据一般都是在DAO层进行处理,可以选择直接使用JDBC进行编程 或者是使用多个DataSource 然后创建多个SessionFact ...

  7. SpringBoot+Poi-tl根据Word模板动态生成word(含动态行表格、合并单元格)

    本编文章继SpringBoot+Poi-tl根据Word模板动态生成word(含动态行表格)文章之后 介绍Poi-tl导出word的延伸功能: 所需依赖以及word模板所属位置 见 SpringBoo ...

  8. springboot+quartz构建定时任务

    springboot+quartz构建定时任务 开发环境 Quartz的3个基本要素 如何使用 引入相关依赖 resource目录下创建quartz.properties quartz需要用到的表 实 ...

  9. SpringBoot2/SpringBoot/Java动态数据源配置、动态连接池配置、多数据源负载均衡

    Java动态数据源配置.动态连接池配置.多数据源负载均衡 大家好,今天给大家推荐一个自产的连接池插件.废话不多说,本文接口分为以下主题: 1. 插件开发背景: 2. 插件提供的能力: 3. 插件的使用 ...

  10. 深入Quartz,优雅地管理你的定时任务

    深入Quartz,优雅地管理你的定时任务 1 初识Quartz 2 Quartz基础使用 2.1 基于时间间隔的定时任务 2.2 基于Cron表达式的定时任务 3 Quartz解读 3.1 Job 3 ...

最新文章

  1. java中Array和ArrayList区别
  2. jsp 跳到servlet路径_直接访问Jsp和从Servlet跳转到Jsp时的路径问题
  3. 如何用jlink+jflash烧写stm32f103CB的option bytes 和程序
  4. 在代理类中引用动态代理
  5. mongodb对数组元素及内嵌文档进行增删改查操作(转)
  6. Android 动态移动控件实现
  7. 客户网站被黑导致CDN加速后打开域名就提示域名纠错
  8. vue自定义组件递归实现树状_一道价值25k的腾讯递归组件面试题(Vue3 + TS 实现)...
  9. 调试中除了在URL上加时间戳外,如何避免js、css被返回304状态?
  10. 高并发程序设计入门(转)
  11. 2022年计算机软件水平考试程序员(初级)练习题及答案
  12. 股市日记之十四:牛二阶段(上篇)
  13. Scrapy爬取当当网畅销图书保存csv格式!最详细的教程!
  14. 打印机可以打印不能扫描怎么弄_惠普打印机可以复印不能扫描怎么操作
  15. signature=7bfc4e6c1dbcfddf5237122a73885e6d,Bending receiver using heat-shrinkable film
  16. 常用物流快递单号自动识别api接口-快递鸟对接
  17. STC用PCA测量脉宽_超快激光脉冲测量和诊断使用自相关仪
  18. CAS算法与ABA问题
  19. 区块链共享数据架构设计
  20. PTA练习:谁先倒.(C语言)

热门文章

  1. php网页制作头部和尾部,用phpcms如何将静态页面制作成企业网站,头部加尾部
  2. java大数据开发是做什么的_3年Java开发转型大数据,如何跳出CRUD舒适区?
  3. php 图像居中裁剪函数,php中自定义图像居中裁剪函数实现的代码案例
  4. python做数据可视化的优势_用Python进行数据可视化的10种方法
  5. python中元组和列表的区别_Python 序列:列表、元组
  6. js获取歌曲时长_小白的js——html播放器(3)
  7. NOWCODER暑期多校第四场F:Beautiful Garden(签到题)题解
  8. 《C++(二)--智能指针》
  9. Mac OS git多次需要输入用户名密码config解决
  10. 393.UTF-8编码验证