Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >SpringBoot+MySQL实现动态定时任务

SpringBoot+MySQL实现动态定时任务

作者头像
科技新语
发布于 2025-05-09 06:24:11
发布于 2025-05-09 06:24:11
14401
代码可运行
举报
运行总次数:1
代码可运行
介绍

一个极简的基于springboot的动态定时任务demo,spring-context模块有对任务调度进行支持,本demo也是基于这个模块进行开发,功能相对简单,用户可以按需改造,比如添加运维界面,在界面上更加灵活的控制任务执行与更新。

基本原理
  • 服务启动后,扫描数据库中的task表,将定时任务添加到调度器中
  • 起一个固定频率执行的ScheduleUpdater任务去扫描数据库,判断任务如有启停、调整cron的话,就更新调度器中的任务。
依赖的核心API

将任务添加到调度器之后,会返回一个ScheduledFuture对象用于控制任务的取消执行。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
java 体验AI代码助手 代码解读复制代码//添加任务到调度器
@see org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler#schedule(java.lang.Runnable, org.springframework.scheduling.Trigger)
//取消任务执行
@see java.util.concurrent.Future#cancel
@see java.util.concurrent.ScheduledFuture
具体实现
数据库表
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
sql 体验AI代码助手 代码解读复制代码-- ----------------------------
-- Table structure for t_scheduler_info
-- ----------------------------
DROP TABLE IF EXISTS `t_scheduler_info`;
CREATE TABLE `t_scheduler_info` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `task_name` varchar(32) NOT NULL,
  `task_code` varchar(64) NOT NULL,
  `task_cron` varchar(32) NOT NULL,
  `state` tinyint DEFAULT 1 NOT  NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='任务表';

INSERT INTO `demo`.`t_scheduler_info` (`id`, `task_name`, `task_code`, `task_cron`, `state`) VALUES ('1', 'foo', 'foo', '0/5 * * * * ?', '1');
INSERT INTO `demo`.`t_scheduler_info` (`id`, `task_name`, `task_code`, `task_cron`, `state`) VALUES ('2', 'bar', 'bar', '0/9 * * * * ?', '0');
INSERT INTO `demo`.`t_scheduler_info` (`id`, `task_name`, `task_code`, `task_cron`, `state`) VALUES ('3', 'didi', 'didi', '0/10 * * * * ?', '0');
maven依赖
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
xml 体验AI代码助手 代码解读复制代码    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.10.RELEASE</version>
        <relativePath/>
    </parent>
    <groupId>com.example</groupId>
    <artifactId>schedule-demo</artifactId>
    <version>1.0.0</version>
    <name>schedule-demo</name>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.22</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
项目启动类及配置类
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
java 体验AI代码助手 代码解读复制代码@SpringBootApplication
@EnableScheduling //开启定时任务支持
public class ScheduleDemoApplication {

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

}

@Configuration
@Slf4j
public class ScheduleConfig implements SchedulingConfigurer {

    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        scheduledTaskRegistrar.setTaskScheduler(taskScheduler());
    }

    @Bean
    public ThreadPoolTaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(10);
        taskScheduler.setWaitForTasksToCompleteOnShutdown(true);
        taskScheduler.setThreadNamePrefix("dynamic_schedule_");
        taskScheduler.setRemoveOnCancelPolicy(true);
        taskScheduler.setErrorHandler(t -> {
            //TODO
            t.printStackTrace();
        });
        return taskScheduler;
    }
}
任务动态更新
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
java 体验AI代码助手 代码解读复制代码@Component
@Slf4j
public final class ScheduleTaskService implements InitializingBean {
    
    //任务注册表,存储所有运行的任务,动态增删基于它来操作
    public final static ConcurrentHashMap<String, dynamicScheduleTask> taskHolder = new ConcurrentHashMap<>();

    @Resource
    private ScheduleInfoDao scheduleInfoDao;

    //拿到所有的ITask对象,转换成Map<TaskCode, ITask>,添加任务时通过TaskCode定位到ITask
    @Resource
    private List<ITask> jobs;
    private Map<String, ITask> jobMap;

    @Resource
    private ThreadPoolTaskScheduler taskScheduler;

    public void newScheduleTask(dynamicScheduleTask scheduleTask) {
        String taskCode = scheduleTask.getTaskCode();
        String taskCron = scheduleTask.getTaskCron();

        // 通过taskCode定位Itask,然后添加调度器
        ITask job = jobMap.get(taskCode);
        TaskPO taskPO = new TaskPO();
        BeanUtils.copyProperties(scheduleTask, taskPO);
        job.setTaskInfo(taskPO);

       // Date executionTime = new CronTrigger(taskCron).nextExecutionTime(new SimpleTriggerContext());
       // log.info("executionTime={}",executionTime);

        ScheduledFuture<?> scheduledFuture = taskScheduler.schedule(job, new CronTrigger(taskCron));
        taskHolder.put(taskCode, new dynamicScheduleTask(taskCode, taskCron, scheduledFuture));
        log.info("new task={} cron={}", taskCode, taskCron);
    }

    public void updateScheduleTask(dynamicScheduleTask newScheduleTask) {
        String taskCode = newScheduleTask.getTaskCode();
        String taskCron = newScheduleTask.getTaskCron();
        Integer state = newScheduleTask.getState();

        if (state == 0) {
            removeScheduleTask(taskCode);
            return;
        }

        dynamicScheduleTask oldScheduleTask = taskHolder.get(taskCode);

        if (oldScheduleTask == null) {
            newScheduleTask(newScheduleTask);
            return;
        }

        if (oldScheduleTask != null && !taskCron.equalsIgnoreCase(oldScheduleTask.getTaskCron())) {
            log.info("update task={},cron={} --> {}", taskCode, oldScheduleTask.getTaskCron(), taskCron);
            removeScheduleTask(taskCode);

            newScheduleTask(new dynamicScheduleTask(taskCode, taskCron));
        }
    }

    public void removeScheduleTask(String taskCode) {
        dynamicScheduleTask oldScheduleTask = taskHolder.get(taskCode);
        if (oldScheduleTask != null) {
            oldScheduleTask.getScheduledFuture().cancel(true);
            taskHolder.remove(taskCode);
            log.info("remove task={}", taskCode);
        }
    }

    public void scheduleAll() {
        List<ScheduleInfoEntity> schedules = (List<ScheduleInfoEntity>) scheduleInfoDao.findAll();
        schedules.stream()
                .filter(task -> task.getState() == 1) //过滤task状态,1为启动
                .forEach(task -> {
                    dynamicScheduleTask dynamicScheduleTask = new dynamicScheduleTask();
                    BeanUtils.copyProperties(task, dynamicScheduleTask);
                    newScheduleTask(dynamicScheduleTask);
                });
    }


    @Override
    public void afterPropertiesSet() {
        jobMap = jobs.stream().collect(Collectors.toMap(ITask::getTaskCode, Function.identity()));
    }
}
启动任务扫描与更新

ScheduleInit ApplicationRunner的实现类,用于服务启动后加载数据库中的任务,以及启动线程定时扫描任务表,更新调度器。

ScheduleUpdater 定时扫描数据库做更新

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
java 体验AI代码助手 代码解读复制代码@Component
public class ScheduleInit implements ApplicationRunner {

    @Resource
    private ScheduleUpdater scheduleUpdater;

    @Resource
    private ScheduleTaskService scheduleTaskService;

    @Resource
    private ThreadPoolTaskScheduler taskScheduler;

    @Value("${task.refresh.interval:5}")
    private int refreshInterval;

    @Override
    public void run(ApplicationArguments args) {
        scheduleTaskService.scheduleAll();

        //task update thread
        taskScheduler.scheduleWithFixedDelay(scheduleUpdater, Duration.ofSeconds(refreshInterval));
    }
}

@Slf4j
@Component
public class ScheduleUpdater implements Runnable {

    @Resource
    private ScheduleInfoDao scheduleInfoDao;

    @Resource
    private ScheduleTaskService scheduleTaskService;

    @Override
    public void run() {
        scheduleInfoDao.findAll().forEach(task -> {
            dynamicScheduleTask dynamicScheduleTask = new dynamicScheduleTask();
            BeanUtils.copyProperties(task, dynamicScheduleTask);
            scheduleTaskService.updateScheduleTask(dynamicScheduleTask);
        });
    }

}
任务基类

ITask 动态任务的基类,项目中的定时任务都基于它实现业务逻辑,如FooTask

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
java 体验AI代码助手 代码解读复制代码public interface ITask extends Runnable {

    /**
     * ScheduleTaskService通过taskCode定位到具体的任务
     */
    String getTaskCode();

    /**
     * 传递数据库中的task信息
     *
     * @param taskPO
     */
    default void setTaskInfo(TaskPO taskPO) {

    }
}

@Component
@Slf4j
public class FooTask implements ITask {

    @Override
    public String getTaskCode() {
        return "foo";
    }

    @Override
    public void run() {
        log.info("I am {} task,cron={}, 呱呱呱", getTaskCode(), taskPO.getTaskCron());
    }


    @Override
    public void setTaskInfo(TaskPO taskPO) {
        this.taskPO = taskPO;
    }

    private TaskPO taskPO;
}

@Component
@Slf4j
public class DiDiTask implements ITask {


    @Override
    public String getTaskCode() {
        return "didi";
    }

    @Override
    public void run() {
        log.info("I am {} task, 滴滴滴", getTaskCode());
    }

//    @Override
//    public void setTaskInfo(TaskPO taskPO) {
//        this.taskPO = taskPO;
//    }
//
//    private TaskPO taskPO;
}
其它的数据库访问层及domain
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
java 体验AI代码助手 代码解读复制代码//CrudRepository
public interface ScheduleInfoDao extends CrudRepository<ScheduleInfoEntity,Integer> {

}

//Entity
@Entity
@Table(name = "t_scheduler_info")
@Data
public class ScheduleInfoEntity {
    @Id
    private Integer id;
    private String taskName;
    private String taskCode;
    private String taskCron;
    private Integer state;
}

//传递给ITask,供业务端灵活使用
@Data
public class TaskPO {
    private Integer id;
    private String taskName;
    private String taskCode;
    private String taskCron;
    private Integer state;
    // other info...
}

//ScheduleTaskService使用的对象,主要是hold ScheduledFuture
@Data
public class dynamicScheduleTask {
    private String taskCode;
    private String taskCron;
    private String taskName;
    private Integer state;

    /**
     * 任务添加到调度器之后会返回该对象,可以用于控制任务的取消
     */
    private ScheduledFuture<?> scheduledFuture;

    public dynamicScheduleTask() {

    }

    public dynamicScheduleTask(String taskCode, String taskCron) {
        this.taskCode = taskCode;
        this.taskCron = taskCron;
    }

    public dynamicScheduleTask(String taskCode, String taskCron, ScheduledFuture<?> scheduledFuture) {
        this.taskCode = taskCode;
        this.taskCron = taskCron;
        this.scheduledFuture = scheduledFuture;
    }

    public String getTaskCode() {
        return taskCode;
    }

}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
properties 体验AI代码助手 代码解读复制代码#数据库配置
# database
spring.datasource.url=jdbc:mysql://localhost:3306/demo?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.druid.initialSize=10
spring.datasource.druid.minIdle=20
spring.datasource.druid.maxActive=20
spring.datasource.druid.query-timeout=60
spring.datasource.druid.max-wait=20000

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
SpringBoot 设置动态定时任务,千万别再写死了~
点击上方“芋道源码”,选择“设为星标” 管她前浪,还是后浪? 能浪的浪,才是好浪! 每天 10:33 更新文章,每天掉亿点点头发... 源码精品专栏 原创 | Java 2021 超神之路,很肝~ 中文详细注释的开源项目 RPC 框架 Dubbo 源码解析 网络应用框架 Netty 源码解析 消息中间件 RocketMQ 源码解析 数据库中间件 Sharding-JDBC 和 MyCAT 源码解析 作业调度中间件 Elastic-Job 源码解析 分布式事务中间件 TCC-Transaction
芋道源码
2022/10/09
1.4K0
SpringBoot 设置动态定时任务,千万别再写死了~
SpringBoot 实现固定、动态定时任务 | 三种实现方式 | 附源代码
背景:📅 最近要用到这个定时任务,之前就简单使用注解的那种方式,需求一变化,就得重新修改。 就想到了动态定时任务,连接数据库来动态选择,这样确实解决了问题。 但是仍然有一个缺陷,就是没法设置任务的执行时间,无法做到像 QQ 发说说那样,给 xdm 祝福生日时,设定说说为晚上00:00发布。 本文就以上三点用自己的思路写了一个小Demo,希望对大家有所帮助。 👩‍💻 封面:来自于校园一角,秋意渐浓,思念渐深。 前言: 阅读完本文:🐱‍👓 知晓 SpringBoot 用注解如何实现定时任务 明白
宁在春
2022/10/31
7.8K0
SpringBoot 实现固定、动态定时任务 | 三种实现方式 | 附源代码
SpringBoot系列之动态定时程序改进版
基于上篇博客,做了一版动态定时程序,然后发现这个定时程序需要在下次执行的时候会加载新的时间,所以如果改了定时程序不能马上触发,所以想到一种方法,在保存定时程序的时候将cron表达式传过去,然后触发定时程序,下面看看怎么实现
SmileNicky
2022/11/18
4750
Spring Boot整合Scheduled定时任务器、整合Quartz定时任务框架
首先说明一下,这里使用的是Springboot2.2.6.RELEASE版本,由于Springboot迭代很快,所以要注意版本问题。
别先生
2020/05/27
1.2K0
Spring Boot整合Scheduled定时任务器、整合Quartz定时任务框架
SpringBoot 定时任务动态管理通用解决方案
一、功能说明 SpringBoot的定时任务的加强工具,实现对SpringBoot原生的定时任务进行动态管理,完全兼容原生@Scheduled注解,无需对原本的定时任务进行修改 二、快速使用 具体的功能已经封装成SpringBoot-starter即插即用 <dependency>     <groupId>com.github.guoyixing</groupId>     <artifactId>spring-boot-starter-super-scheduled</artifactId>     <
java思维导图
2022/03/04
8880
重学SpringBoot3-整合Quartz定时任务
Quartz 是一个开源的任务调度框架,用于在应用程序中创建、管理和调度定时任务。将 Quartz 和 Spring Boot 3 结合,可以轻松实现定时任务的灵活管理。本文将详细介绍如何在 Spring Boot 3 项目中集成 Quartz Scheduler 并实现示例任务的调度。
CoderJia
2024/11/14
3060
重学SpringBoot3-整合Quartz定时任务
SpringBoot系列之使用Spring Task实现定时任务
定时任务是企业开发中很常用的,比如定时推送一些接口数据,在java中实现定时任务的方法有Spring Task、Quartz等等框架,也有JDK自带的ScheduledExecutorService、Timer
SmileNicky
2020/07/23
1.2K0
springboot(九):定时任务
在我们的项目开发过程中,经常需要定时任务来帮助我们来做一些内容,springboot默认已经帮我们实行了,只需要添加相应的注解就可以实现 1、pom包配置 pom包里面只需要引入springboot starter包即可 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </de
纯洁的微笑
2018/04/19
6960
实践:SpringBoot实现定时任务的动态增删启停
我以为动态停启定时任务一般用quartz,没想到还可以通过ScheduledTaskRegistrar来拓展。但是分布式场景,建议还是用quartz吧!
乔戈里
2020/08/06
1.2K0
SpringBoot Quartz实现动态定时任务
JobDetail:基于Job,进一步封装。其中关联一个Job,并为Job指定更详细的信息。
jwangkun
2021/12/23
2.3K0
SpringBoot Quartz实现动态定时任务
SpringBoot ( 九 ) :定时任务
在我们的项目开发过程中,经常需要定时任务来帮助我们来做一些内容,springboot默认已经帮我们实行了,只需要添加相应的注解就可以实现。
前朝楚水
2018/07/26
3040
SpringBoot中的定时任务的同步与异步
定时任务调度功能在我们的开发中是非常常见的,随便举几个例子:定时清除一些过期的数据,定时发送邮件等等,实现定时任务调度的方式也十分多样,本篇文章主要学习各种实现定时任务调度方式的优缺点,以便为日后选择的时候提供一定的参考。
烂猪皮
2023/09/04
1.1K0
SpringBoot中的定时任务的同步与异步
SpringBoot整合Quartz实现定时任务(单任务、多任务)
前言 Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的程序。Jobs可以做成标准的Java组件或 EJBs 一、添加依赖 Code <dependencies> <dependency> <groupId>org.springframework.boot</groupId>
MiChong
2020/09/24
2.6K0
Spring的任务调度处理
Spring封装了JDK的任务调度线程池和任务调用,并使用标签就可以开启一个任务调用。
算法之名
2020/10/26
5730
Spring的任务调度处理
Java之定时任务全家桶
由此看出走不同的线程执行,不同的线程执行的好处是,如果某一个线程挂掉后,并不会阻塞导致其它定时任务无法执行。
BUG弄潮儿
2021/03/04
6110
Spring 定时任务
本文参考自Spring官方文档 34. Task Execution and Scheduling。
乐百川
2022/05/05
1K0
玩转定时任务,下次别为了修改定时任务而不断停止和重新启动项目了
相信大家在学习苍穹外卖的时候,就使用过定时任务。但是那个时候我们使用的定时任务的方式还比较死板:
程序员牛肉
2024/09/26
3510
玩转定时任务,下次别为了修改定时任务而不断停止和重新启动项目了
一起来学SpringBoot | 第十六篇:定时任务详解
在我们日常开发中,经常会遇到 数据定时增量同步、 定时发送邮件、 爬虫定时抓取 等需求;这时我们可以采用 定时任务的方式去进行工作.....
battcn
2018/08/03
5610
SpringBoot中实现定时任务(Quartz)
Spring 3.0以后自带了 task 调度工具,使用比 Quartz简单方便,使用 @Scheduled 注解。
终有救赎
2023/10/22
6430
SpringBoot中实现定时任务(Quartz)
通过源码理解Spring中@Scheduled的实现原理并且实现调度任务动态装载
最近的新项目和数据同步相关,有定时调度的需求。之前一直有使用过Quartz、XXL-Job、Easy Scheduler等调度框架,后来越发觉得这些框架太重量级了,于是想到了Spring内置的Scheduling模块。而原生的Scheduling模块只是内存态的调度模块,不支持任务的持久化或者配置(配置任务通过@Scheduled注解进行硬编码,不能抽离到类之外),因此考虑理解Scheduling模块的底层原理,并且基于此造一个简单的轮子,使之支持调度任务配置:通过配置文件或者JDBC数据源。
Throwable
2020/06/23
2.6K0
通过源码理解Spring中@Scheduled的实现原理并且实现调度任务动态装载
推荐阅读
相关推荐
SpringBoot 设置动态定时任务,千万别再写死了~
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验