Spring Boot 动态定时任务管理系统(轻量级实现)

news2024/12/25 5:55:35

Spring Boot项目中,实现动态增删和启停定时任务的功能对于许多应用场景来说至关重要。虽然Quartz框架是一个广泛使用的解决方案,但其复杂性和重量级特性可能使得项目变得臃肿和难以维护。为了解决这个问题,本项目旨在实现一个轻量级的定时任务管理系统,不依赖外部框架,仅利用Spring Boot和Java标准库的功能,特别适用于单体项目。

数据库

DROP TABLE IF EXISTS `scheduled_jobs`;
CREATE TABLE `scheduled_jobs`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '任务ID,唯一标识每个定时任务',
  `method_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '方法名称,执行定时任务时调用的方法名',
  `cron_expression` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'cron表达式,定义定时任务的执行时间规则',
  `status` int(11) NOT NULL DEFAULT 1 COMMENT '任务状态,1为正常运行,0为暂停',
  `content` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '1' COMMENT '任务正文,具体需要执行的内筒',
  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '任务的备注信息,描述任务的其他细节',
  `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`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1391804419 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

POM依赖

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


        <!-- mybatis-plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.9</version>
        </dependency>

        <!--lombok依赖-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!--MySQL数据库的依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.33</version>
        </dependency>

        <!--hutool工具依赖-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.26</version>
        </dependency>

项目启动类

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.sws.dynamicscheduler.mapper")
public class DynamicSchedulerApplication {

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

}

任务管理器类

import com.sws.dynamicscheduler.mapper.ScheduledJobMapper;
import com.sws.dynamicscheduler.model.ScheduledJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ScheduledFuture;

@Service
@Slf4j
public class DynamicTaskManager {

    private final ThreadPoolTaskScheduler taskScheduler;
    private final ScheduledJobMapper scheduledJobMapper;
    private final Map<String, ScheduledFuture<?>> scheduledTasks;

    @Autowired
    public DynamicTaskManager(ScheduledJobMapper scheduledJobMapper, ThreadPoolTaskScheduler taskScheduler) {
        this.scheduledJobMapper = scheduledJobMapper;
        this.taskScheduler = taskScheduler;
        this.scheduledTasks = new HashMap<>();
    }


    @PostConstruct
    public void init() {
        // 初始化时加载数据库中的所有定时任务并调度
        List<ScheduledJob> jobs = scheduledJobMapper.selectList(null);
        for (ScheduledJob job : jobs) {
            if (job.getStatus() == 1) {
                addTask(job.getCronExpression(), job.getMethodName(),job.getContent(), job.getId().toString());
            }
        }
    }

    // 动态新增任务
    public String addTask(String cronExpression, String methodName, String content,String taskId) {
        Runnable task = () -> {
            // 执行定时任务的逻辑
           log.info("执行了定时任务:{},执行内容是:{}",methodName,content);
        };

        ScheduledFuture<?> scheduledFuture = taskScheduler.schedule(task, new CronTrigger(cronExpression));
        scheduledTasks.put(taskId, scheduledFuture);
        return taskId;
    }

    // 动态删除任务
    public void removeTask(String taskId) {
        ScheduledFuture<?> scheduledFuture = scheduledTasks.get(taskId);
        if (scheduledFuture != null) {
            scheduledFuture.cancel(false); // 取消任务
            scheduledTasks.remove(taskId); // 从集合中删除
        }
    }

    // 停止任务
    public void stopTask(Integer jobId) {
        Optional<ScheduledJob> jobOptional = Optional.ofNullable(scheduledJobMapper.selectById(jobId));
        jobOptional.ifPresent(job -> {
            job.setStatus(0); // 设置为暂停状态
            scheduledJobMapper.insert(job);
            removeTask(jobId.toString()); // 停止定时任务
        });
    }

    // 恢复任务
    public void resumeTask(Integer jobId) {
        Optional<ScheduledJob> jobOptional = Optional.ofNullable(scheduledJobMapper.selectById(jobId));
        jobOptional.ifPresent(job -> {
            job.setStatus(1); // 设置为正常状态
            scheduledJobMapper.insert(job);
            addTask(job.getCronExpression(), job.getMethodName(), job.getContent(),jobId.toString()); // 恢复定时任务
        });
    }
}

实体类

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;


import java.util.Date;

@Data
@TableName("`scheduled_jobs`")
public class ScheduledJob {

    private Integer id;                // 任务ID,唯一标识每个定时任务
    private String methodName;         // 方法名称,执行定时任务时调用的方法名
    private String cronExpression;    // cron表达式,定义定时任务的执行时间规则
    private Integer status;            // 任务状态,1为正常运行,0为暂停
    private String content;           // 任务正文,具体需要执行的内容
    private String remark;             // 任务的备注信息,描述任务的其他细节
    private Date createTime;           // 任务创建时间,自动设置为当前时间
    private Date updateTime;           // 任务最后更新时间,每次更新时自动更新时间


}

配置类

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

@Configuration
public class SchedulerConfig {

    @Bean
    public ThreadPoolTaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        scheduler.setPoolSize(10); // 设置线程池的大小
        scheduler.setThreadNamePrefix("scheduled-task-"); // 设置线程名称前缀
        return scheduler;
    }
}

Mapper类

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.sws.dynamicscheduler.model.ScheduledJob;

public interface ScheduledJobMapper extends BaseMapper<ScheduledJob> {

}

控制类

import com.sws.dynamicscheduler.model.ScheduledJob;
import com.sws.dynamicscheduler.mapper.ScheduledJobMapper;
import com.sws.dynamicscheduler.scheduler.DynamicTaskManager;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.List;

@RestController
@RequestMapping("/tasks")
public class TaskController {

    @Resource
    DynamicTaskManager taskManager;

    @Resource
    ScheduledJobMapper scheduledJobMapper;


    // 动态添加任务
    @PostMapping("/add")
    public String addTask(@RequestParam String cronExpression, @RequestParam String methodName,@RequestParam String content) {
        ScheduledJob job = new ScheduledJob();
        job.setMethodName(methodName);
        job.setContent(content);
        job.setCronExpression(cronExpression);
        job.setStatus(1);
        scheduledJobMapper.insert(job); // 保存到数据库

        // 添加定时任务
        return taskManager.addTask(cronExpression, methodName,content,job.getId().toString());
    }

    // 动态删除任务
    @DeleteMapping("/remove")
    public void removeTask(@RequestParam Integer jobId) {
        taskManager.removeTask(jobId.toString());
        scheduledJobMapper.deleteById(jobId);
    }

    // 暂停任务
    @PostMapping("/pause")
    public void pauseTask(@RequestParam Integer jobId) {
        taskManager.stopTask(jobId);
    }

    // 恢复任务
    @PostMapping("/resume")
    public void resumeTask(@RequestParam Integer jobId) {
        taskManager.resumeTask(jobId);
    }

    // 获取所有任务
    @GetMapping("/all")
    public List<ScheduledJob> getAllTasks() {
        return scheduledJobMapper.selectList(null);
    }
}

项目地址:神蛐/DynamicScheduler

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

提高保养效率:4S店预约系统的设计与开发

3.1可行性分析 开发者在进行开发系统之前&#xff0c;都需要进行可行性分析&#xff0c;保证该系统能够被成功开发出来。 3.1.1技术可行性 开发该4S店预约保养系统所采用的技术是vue和MYSQL数据库。计算机专业的学生在学校期间已经比较系统的学习了很多编程方面的知识&#xff…

网络下载ts流媒体

网络下载ts流媒体 查看下载排序合并 很多视频网站&#xff0c;尤其是微信小程序中的长视频无法获取到准确视频地址&#xff0c;只能抓取到.ts片段地址&#xff0c;下载后发现基本都是5~8秒时长。 例如&#xff1a; 我们需要将以上地址片段全部下载后排序后再合成新的长视频。 …

(叁)前端实现加密的方式:AES 加密(实现的两种方式)

前端实现加密&#xff1a; Ⅰ、AES 加密&#xff1a;1、使用 AES 加密前的准备工作&#xff1a;其一、安装 crypto-js &#xff1a;A、安装命令&#xff1a;B、成功安装后的截图&#xff1a; 2、实现 AES 加密的方式一&#xff1a;其一、在项目中创建 crypto.js 文件&#xff1…

kubernates实战

使用k8s来部署tomcat 1、创建一个部署&#xff0c;并指定镜像地址 kubectl create deployment tomcat6 --imagetomcat:6.0.53-jre82、查看部署pod状态 kubectl get pods # 获取default名称空间下的pods kubectl get pods --all-namespaces # 获取所有名称空间下的pods kubect…

自动化测试框架playwright 常见问题和解决方案

自动化课程已经讲完了playwright框架&#xff0c;很多同学跃跃欲试&#xff0c;所谓实践出真知&#xff0c;这不在实践中就要到了一些问题&#xff0c;小编也给大家整理出来了&#xff0c;送个有需要的同学&#xff0c;记得点赞收藏哦~~ 01安装问题 问题描述&#xff1a; 在安…

清华、智谱团队:探索 RLHF 的 scaling laws

基于人类反馈的强化学习&#xff08;RLHF&#xff09;是优化大语言模型&#xff08;LLM&#xff09;行为的关键技术&#xff0c;能够让模型更符合人类偏好和需求&#xff0c;提升生成质量。 然而&#xff0c;目前关于 RLHF 的 scaling&#xff08;扩展&#xff09;潜力研究仍然…

八股(One Day one)

最近老是看到一些面试的视频&#xff0c;对于视频内部面试所提到的八股文&#xff0c;感觉是知道是什么&#xff0c;但是要说的话&#xff0c;却又不知道该怎么说&#xff08;要不咋称之为八股文呢&#xff09;&#xff0c;所以就想到写一篇八股文总结的博客&#xff0c;以便进…

springboot创建web项目

一、创建项目 二、导入依赖&#xff08;pom.xml&#xff09; <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schem…

LeetCode 343.整数拆分

1.题目要求: 2.题目代码: class Solution { public:int integerBreak(int n) {//先确定dp数组vector<int> dp;//1.确定dp数组的含义//2.确定dp的递推公式//3.初始化dp数组//4.遍历顺序dp.resize(n 1);dp[0] 0;dp[1] 0;dp[2] 1;for(int i 3;i < n;i){for(int j …

Postman最新详细安装及使用教程【附安装包】

一、Postman介绍 ‌Postman是一个功能强大的API测试工具&#xff0c;主要用于模拟和测试各种HTTP请求&#xff0c;支持GET、POST、PUT、DELETE等多种请求方法。‌通过Postman&#xff0c;用户可以发送请求并查看返回的响应&#xff0c;检查响应的内容和状态&#xff0c;从而验…

开源 JS PDF 库比较

原文查看&#xff1a;开源JavaScript PDF Library对比 对于需要高性能、复杂功能或强大支持处理复杂 PDF 的项目&#xff0c;建议选择商业​​ PDF 库, 如ComPDFKit for Web。但是&#xff0c;如果您的目标只是在 Web 应用程序中显示 PDF&#xff0c;则可以使用几个可靠的开源…

如何利用AWS监听存储桶并上传到tg bot

业务描述&#xff1a; 需要监听aws的存储中的最新消息&#xff0c;发送新的消息推送到指定tg的频道。 主要流程&#xff1a; 1.上传消息到s3存储桶&#xff08;不做具体描述&#xff09; 2.通过aws的lambda监听s3存储桶的最新消息&#xff08;txt文件&#xff09; 3.将txt文件…

数据结构:双向循坏链表

目录 1.1双向循环链表的结构 2.双向链表功能的实现 2.1初始化链表 2.2销毁链表 2.3创建结点 2.4打印链表 2.5链表查找 2.6链表在pos的前面进行插入 2.7链表删除pos位置的节点 2.8链表的头插&#xff0c;头删 &#xff0c;尾插&#xff0c;尾删 1.1双向循环链表的结构 …

大数据技术原理与应用期末复习-代码

RDD&#xff1a; // 导入SparkConf和SparkContext类&#xff0c;用于配置和创建Spark上下文 import org.apache.spark.{SparkConf, SparkContext}// 定义一个名为TopN的对象 object TopN {def main(args: Array[String]): Unit {// 创建一个新的SparkConf对象&#xff0c;并设…

CNN和Transfomer介绍

文章目录 CNN和Transfomer介绍CNN和Transfomer的区别1. **基本概念**2. **数据处理方式**3. **模型结构差异**4. **应用场景区别** 自注意力机制1. **自注意力机制的概念**2. **自注意力机制的实现步骤**3. **自注意力机制的优势** Transformer结构组成1. **多头注意力层&#…

ArcGIS Pro 3.4新功能3:空间统计新特性,基于森林和增强分类与回归,过滤空间自相关

目录 应用 1&#xff1a;它是相关性还是托布勒第一定律&#xff1f; 应用 2&#xff1a;将空间带入非空间模型 结论 在 ArcGIS Pro 3.4 中&#xff0c;我们在新的空间组件实用程序&#xff08;Moran 特征向量&#xff09;工具集中发布了一个新工具 - 从字段过滤空间自相关。…

webserver log日志系统的实现

参考博客&#xff1a;https://blog.csdn.net/weixin_51322383/article/details/130474753 https://zhuanlan.zhihu.com/p/721880618 阻塞队列blockqueue 1、阻塞队列的设计流程是什么样的 它的底层是用deque进行管理的 阻塞队列主要是围绕着生产者消费者模式进行多线程的同步和…

深度学习实战之超分辨率算法(tensorflow)——ESPCN

espcn原理算法请参考上一篇论文&#xff0c;这里主要给实现。 数据集如下&#xff1a;尺寸相等即可 针对数据集&#xff0c;生成样本代码preeate_data.py import imageio from scipy import misc, ndimage import numpy as np import imghdr import shutil import os import…

Unity3d 基于UGUI和VideoPlayer 实现一个多功能视频播放器功能(含源码)

前言 随着Unity3d引擎在数字沙盘、智慧工厂、数字孪生等场景的广泛应用&#xff0c;视频已成为系统程序中展示时&#xff0c;不可或缺的一部分。在 Unity3d 中&#xff0c;我们可以通过强大的 VideoPlayer 组件和灵活的 UGUI 系统&#xff0c;将视频播放功能无缝集成到用户界面…

WebGAL 项目下载及安装教程

WebGAL 项目下载及安装教程 WebGAL A brand new web Visual Novel engine | 全新的网页端视觉小说引擎 [这里是图片001] 项目地址: https://gitcode.com/gh_mirrors/web/WebGAL 1、项目介绍 WebGAL 是一个全新的网页端视觉小说引擎&#xff0c;旨在提供美观、功能强大且易于…