Spring Boot:打造动态定时任务,开启灵活调度之旅

一、描述

在 Spring Boot 中设置动态定时任务是一种非常实用的功能,可以根据实际需求在运行时动态地调整定时任务的执行时间、频率等参数。以下是对 Spring Boot 设置动态定时任务的简单介绍:

1、传统定时任务的局限性

在传统的 Spring Boot 定时任务中,通常是通过在方法上使用@Scheduled注解来设置固定的执行时间和频率。这种方式在任务的执行计划相对固定的情况下是有效的,但在一些需要动态调整任务执行时间的场景中就显得不够灵活。例如,在一个电商系统中,可能需要根据不同的促销活动时间动态地调整订单处理任务的执行时间;在一个监控系统中,可能需要根据实时的系统负载情况动态地调整数据采集任务的执行频率。

2、动态定时任务的实现方式

  1. 使用数据库存储任务配置信息:

    • 可以在数据库中创建一个任务配置表,存储任务的名称、执行时间、执行频率等信息。
    • 在 Spring Boot 应用启动时,读取数据库中的任务配置信息,并根据这些信息创建定时任务。
    • 当需要动态调整任务执行时间时,可以通过修改数据库中的任务配置信息,然后通知 Spring Boot 应用重新读取任务配置,从而实现动态调整定时任务的目的。
  2. 使用配置文件动态加载:

    • 在 Spring Boot 的配置文件(如 application.properties 或 application.yml)中,可以定义一些动态的配置参数,用于设置定时任务的执行时间和频率。
    • 通过使用 Spring Boot 的配置文件加载机制,可以在运行时动态地修改配置文件中的参数,然后通知 Spring Boot 应用重新加载配置文件,从而实现动态调整定时任务的目的。
  3. 使用管理界面进行动态配置:

    • 可以开发一个管理界面,让管理员可以在界面上直接设置定时任务的执行时间和频率。
    • 管理界面可以通过调用 Spring Boot 应用提供的 API 来修改任务配置信息,然后通知应用重新读取任务配置,从而实现动态调整定时任务的目的。

3、动态定时任务的优势

  1. 灵活性高:可以根据实际需求在运行时动态地调整定时任务的执行时间和频率,满足不同场景下的需求。
  2. 易于管理:通过数据库、配置文件或管理界面等方式,可以方便地管理定时任务的配置信息,提高系统的可维护性。
  3. 实时响应:可以根据实时的业务需求和系统状态动态地调整定时任务,提高系统的响应能力和效率。

二、案例

1、第一种写法

1、新建spring boot项目

添加相应的依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-log4j2</artifactId>
        <optional>true</optional>
    </dependency>

    <!-- spring boot 2.3版本后,如果需要使用校验,需手动导入validation包-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>
2、启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
 
/**
 * @author wl
 */
@EnableScheduling
@SpringBootApplication
public class Application {
 
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
        System.out.println("(*^▽^*)启动成功!!!(〃'▽'〃)");
    }
}
3、配置文件application.yml

定义服务端口

server:
  port: 8089

定时任务执行时间配置文件:task-config.ini:

myPrintTime.cron=0/10 * * * * ?

4、定时任务执行类:
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.Date;

@Data
@Slf4j
@Component
@PropertySource("classpath:/my-task-config.ini")
public class CustomScheduleTask implements SchedulingConfigurer {

    @Value("${myPrintTime.cron}")
    private String cron;

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.addTriggerTask(() -> {
            log.info("Custom task executed at: {}", LocalDateTime.now());
        }, new Trigger() {
            @Override
            public Date nextExecutionTime(TriggerContext triggerContext) {
                // 使用CronTrigger触发器,可动态修改cron表达式来操作循环规则
                CronTrigger cronTrigger = new CronTrigger(cron);
                Date nextExecutionTime = cronTrigger.nextExecutionTime(triggerContext);
                return nextExecutionTime;
            }
        });
    }
}
5、controller

编写一个接口,构建动态定时任务管理接口

import com.wl.demo.task.ScheduleTask;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;



@Slf4j
@RestController
@RequestMapping("/test")
public class TestController {

    private final ScheduleTask scheduleTask;

    @Autowired
    public TestController(ScheduleTask scheduleTask) {
        this.scheduleTask = scheduleTask;
    }

    @GetMapping("/updateCron")
    public ResponseEntity<String> updateCron(String cron) {
        log.info("new cron :{}", cron);
        scheduleTask.setCron(cron);
        return new ResponseEntity<>("定时任务 cron 表达式更新成功", HttpStatus.OK);
    }
}

启动项目,可以看到任务每10秒执行一次

也可以通过url地址修改任务时间,localhost:8089ytest/updateCron?cron=0/15 * * * *?


6、CronTrigger触发器

除了上面的借助cron表达式的方法,还有另一种触发器,区别于CronTrigger触发器,该触发器可随意设置循环间隔时间,不像cron表达式只能定义小于等于间隔59秒,在这个新的定时任务类中,我们可以从不同的配置文件中读取 cron 表达式,并且设置了一个不同的默认循环时间。根据实际需求,可以灵活地调整 cron 表达式和循环时间间隔。

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.scheduling.support.PeriodicTrigger;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.Date;

@Data
@Slf4j
@Component
@PropertySource("classpath:/my-task-config.ini")
public class NewScheduleTask implements SchedulingConfigurer {

    @Value("${myPrintTime.cron}")
    private String cron;

    private Long customTimer = 15000L;

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.addTriggerTask(() -> {
            log.info("New task execution time: {}", LocalDateTime.now());
        }, new Trigger() {
            @Override
            public Date nextExecutionTime(TriggerContext triggerContext) {
                // 使用 CronTrigger 或根据需求切换
                CronTrigger cronTrigger = new CronTrigger(cron);
                Date nextExecutionTime = cronTrigger.nextExecutionTime(triggerContext);
                if (nextExecutionTime == null) {
                    PeriodicTrigger periodicTrigger = new PeriodicTrigger(customTimer);
                    return periodicTrigger.nextExecutionTime(triggerContext);
                }
                return nextExecutionTime;
            }
        });
    }
}

修改时间的接口

import com.wl.demo.task.ScheduleTask;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;



@Slf4j
@RestController
@RequestMapping("/test")
public class TestController {

    private final ScheduleTask scheduleTask;

    @Autowired
    public TestController(ScheduleTask scheduleTask) {
        this.scheduleTask = scheduleTask;
    }

    @GetMapping("/updateCron")
    public ResponseEntity<String> updateCron(String cron) {
        log.info("new cron :{}", cron);
        scheduleTask.setCron(cron);
        return new ResponseEntity<>("定时任务 cron 表达式更新成功", HttpStatus.OK);
    }

    @GetMapping("/updateTimer")
    public String updateTimer(Long timer) {
        log.info("new timer :{}", timer);
        scheduleTask.setTimer(timer);
        return "ok";
    }
}

运行结果

2、第二种写法

1、添加依赖

和前面一样,在 Maven 项目的pom.xml文件中添加以下依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
2、创建定时任务执行接口和实现类

首先定义一个定时任务执行的接口:

public interface ScheduledTaskExecutor {
    void execute();
}

然后创建一个实现类:

import org.springframework.stereotype.Component;

@Component
public class DynamicTaskExecutor implements ScheduledTaskExecutor {

    @Override
    public void execute() {
        System.out.println("定时任务执行:" + new java.util.Date());
    }
}
3、配置定时任务调度器

创建一个定时任务调度器类

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;

import java.util.Date;

@Configuration
@EnableScheduling
public class ScheduledTaskConfig implements SchedulingConfigurer {

    @Autowired
    private DynamicTaskExecutor dynamicTaskExecutor;

    private String cronExpression = "0/5 * * * *?"; // 默认每 5 秒执行一次

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.addTriggerTask(
                () -> dynamicTaskExecutor.execute(),
                triggerContext -> {
                    CronTrigger trigger = new CronTrigger(cronExpression);
                    return trigger.nextExecutionTime(triggerContext);
                }
        );
    }

    // 提供一个方法用于动态修改定时任务的表达式
    public void setCronExpression(String expression) {
        cronExpression = expression;
    }
}
4、动态修改定时任务

可以在其他地方调用ScheduledTaskConfigsetCronExpression()方法来动态修改定时任务的执行时间表达式。

例如:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class SchedulingService {

    @Autowired
    private ScheduledTaskConfig scheduledTaskConfig;

    public void changeCronExpression(String newExpression) {
        scheduledTaskConfig.setCronExpression(newExpression);
    }
}

通过这种方式,你可以在运行时动态地调整定时任务的执行时间,提高应用的灵活性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/906687.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Lua 从基础入门到精通(非常详细)

目录 什么是 Lua&#xff1f; Lua 环境安装 Lua基本语法 注释 数据类型 nil&#xff08;空&#xff09; Boolean number&#xff08;数字&#xff09; string&#xff08;字符串&#xff09; function&#xff08;函数&#xff09; userdata thread table&#xff…

PostgreSQL 到 PostgreSQL 数据迁移同步

简述 PostgreSQL 是一个历史悠久且广泛使用的数据库&#xff0c;不仅具备标准的关系型数据库能力&#xff0c;还具有相当不错的复杂 SQL 执行能力。用户常常会将 PostgreSQL 应用于在线事务型业务&#xff0c;以及部分数据分析工作&#xff0c;所以 PostgreSQL 到 PostgreSQL …

GESP4级考试语法知识(捕捉异常)

参考程序代码&#xff1a; #include <iostream> using namespace std;double divide(double a, double b) {if (b 0) {throw "Division by zero error"; // 抛出异常}return a / b; }int main() {double num1, num2;cout << "Enter two numbers:…

新老项目不同node版本,使用nvm控制node版本切换(mac、window)

window系统电脑的链接&#xff1a;https://blog.csdn.net/qq_40269801/article/details/136450961 以下是mac版本的操作方式&#xff1a; 1、打开终端 克隆 NVM 仓库&#xff1a; git clone https://github.com/nvm-sh/nvm.git ~/.nvm 2、运行安装脚本&#xff1a; cd ~/.n…

HTTP与HTTPS协议

HTTP协议&#xff0c;即超文本传输协议&#xff08;HyperText Transfer Protocol&#xff09;&#xff0c;是互联网中一种用于在Web浏览器与Web服务器之间传输数据的应用层协议。它的核心理念是提供一种简单、灵活的方式来请求和响应信息&#xff0c;是现代万维网的基础。 1. 什…

R语言机器学习与临床预测模型79--机器学习总览

R小盐准备介绍R语言机器学习与预测模型的学习笔记 你想要的R语言学习资料都在这里&#xff0c; 快来收藏关注【科研私家菜】 01 机器学习分类 机器学习模型主要分为有监督、无监督和强化学习方法。 监督学习 监督学习是教师向学生提供关于他们在考试中是否表现良好的反馈。其中…

Diving into the STM32 HAL-----HAL_GPIO

1、怎么看待外设&#xff1a; 从总线连接的角度看&#xff0c;外设和Core、DMA通过总线交换数据&#xff0c;正所谓要想富先修路。要注意&#xff0c;这些总线中的每一个都连接到不同的时钟源&#xff0c;这些时钟源决定了连接到该总线的外设操作的最大速度。 从内存分配的角度…

FlinkCDC-MYSQL批量写入

一、运行环境 &#xff08;1&#xff09;Flink&#xff1a;1.17.2 &#xff08;2&#xff09;Scala&#xff1a;2.12.20 &#xff08;3&#xff09;Mysql&#xff1a;5.7.43 ##开启binlog 二、代码示例 思路&#xff1a;通过滚动窗口收集一批数据推给sink消费。binlog日志对…

集合(数组、链表、map)

目录 Collection包结构 和collections区别 List 数组和arrayList 区别 数组下标为什么从0开始&#xff1f; ArrayList 动态数组 LinkedList双向链表增删快 增删快 链表 单链表和双链表区别 Arraylist VS LinkedList 区别 数组和List之间转换 ArrayList 、LinkedList…

多线程和线程同步基础篇学习笔记(Linux)

大丙老师教学视频&#xff1a;10-线程死锁_哔哩哔哩_bilibili 目录 大丙老师教学视频&#xff1a;10-线程死锁_哔哩哔哩_bilibili 线程概念 为什么要有线程 线程和进程的区别 在处理多任务的时候为什么线程数量不是越多越好? Linux提供的线程API 主要接口 线程创建 pth…

希望十一月对我好一点:C++之多态(2)--多态的原理(部分)

多态的原理 虚函数表指针 下⾯编译为32位程序的运⾏结果是什么&#xff08;&#xff09; A. 编译报错 B.运⾏报错C.8 D.12 class Base{public:virtual void Func1(){cout << "Func1()" << endl;}protected:int _b 1;char _ch x;};int main(){Base b…

Java: 遍历 Map

Java: 遍历 Map package animals;import java.util.HashMap; import java.util.Iterator; import java.util.Map;/*** Description :** author : HMF* Date : Created in 15:33 2024/11/1* version :*/ public class Test002 {public static void main(String[] args){Map<S…

基于vue+neo4j 的中药方剂知识图谱可视化系统

前言 历时一周时间&#xff0c;中药大数据R02系统中药开发完毕&#xff0c;该系统通过scrapy工程获取中药数据&#xff0c;使用python pandas预处理数据生成知识图谱和其他相关数据&#xff0c;利用vuespringbootneo4jmysql 开发系统&#xff0c;具体功能请看本文介绍。 简要…

01.如何用DDD重构老项目

学习资料来源&#xff1a;DDD独家秘籍视频合集 https://space.bilibili.com/24690212/channel/collectiondetail?sid1940048&ctype0 文章目录 动机DDD与重构实践重构? 重写从一开始就采用DDD重构步骤1. 添加领域模块2.分离出有价值的代码3.迁移到领域模块4.重复2,3 动机 …

WPF+MVVM案例实战(十八)- 自定义字体图标按钮的封装与实现(ABD类)

文章目录 1、案例效果1、按钮分类2、ABD类按钮实现描述1.文件创建与代码实现2、样式引用与控件封装3、按钮案例演示1、页面实现与文件创建2、运行效果如下3、总结4、源代码获取1、案例效果 1、按钮分类 在WPF开发中,最常见的就是按钮的使用,这里我们总结以下大概的按钮种类,…

Python 5个数据容器

列表&#xff08;list&#xff09; 列表的定义 定义空列表&#xff1a; 变量名 [] 或 变量名 list() 定义变量&#xff1a; 变量名 [元素1&#xff0c;元素2&#xff0c;元素3&#xff0c;... ] 取出列表元素 列表 [下标索引] 从前向后&#xff0c;从0开始&#xff…

使用语言模型进行文本摘要的五个级别(llm)

视频链接&#xff1a;5 Levels Of LLM Summarizing: Novice to Expert

Qt5.15.x源码编译

介绍&#xff1a; QT5.15以上版本已经不提供现成的集成软件了。所以当我们项目中需要用到5.15以上的版本时&#xff0c;只能自己对源码进行编译来生成一个环境了&#xff08;Qt提供了在线升级&#xff0c;但是在线升级中没有MinGW版本了&#xff09; 背景&#xff1a; 我们想要…

Ubuntu 系统、Docker配置、Docker的常用软件配置(下)

前言 书接上文&#xff0c;现在操作系统已经有了&#xff0c;作为程序的载体Docker也安装配置好了&#xff0c;接下来我们需要让Docker发挥它的法力了。 Docker常用软件的安装 1.Redis 缓存安装 1.1 下载 docker pull redis:7.4.1 #可改为自己需要的版本 1.2 创建本地目录存储…

Redis全系列学习基础篇之位图(bitmap)常用命令的解析

文章目录 描述常用命令及解析常用命令解析 应用场景统计不确定时间周期内用户登录情况思路分析实现 统计某一特定时间内活跃用户(登录一次即算活跃)的数量思路分析与实现 描述 bitmap是redis封装的用于针对位(bit)的操作,其特点是计算效率高&#xff0c;占用空间少,常被用来统计…