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>
项目启动类
java">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);}}
任务管理器类
java">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;@Autowiredpublic DynamicTaskManager(ScheduledJobMapper scheduledJobMapper, ThreadPoolTaskScheduler taskScheduler) {this.scheduledJobMapper = scheduledJobMapper;this.taskScheduler = taskScheduler;this.scheduledTasks = new HashMap<>();}@PostConstructpublic 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()); // 恢复定时任务});}
}
实体类
java">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; // 任务最后更新时间,每次更新时自动更新时间}
配置类
java">import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;@Configuration
public class SchedulerConfig {@Beanpublic ThreadPoolTaskScheduler taskScheduler() {ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();scheduler.setPoolSize(10); // 设置线程池的大小scheduler.setThreadNamePrefix("scheduled-task-"); // 设置线程名称前缀return scheduler;}
}
Mapper类
java">import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.sws.dynamicscheduler.model.ScheduledJob;public interface ScheduledJobMapper extends BaseMapper<ScheduledJob> {}
控制类
java">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 {@ResourceDynamicTaskManager taskManager;@ResourceScheduledJobMapper 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