java实现动态定时任务调度
·
| 🎈边走、边悟🎈迟早会好 |
本文展示了一个基于Spring Boot的动态任务调度系统实现,包含核心代码片段。系统采用MyBatis-Plus进行数据持久化,通过ScheduledTaskRegistrar实现动态任务管理,支持以下功能:
- 任务实体类定义(含cron表达式、状态等字段)
- 任务CRUD及状态管理接口(启停/手动执行)
- 动态任务配置(启动时加载+运行时注册)
- 处理器工厂模式(TaskHandlerFactory)实现不同任务类型的执行逻辑
- 基于权限控制的RESTful API设计(@PreAuthorize) 系统亮点包括线程安全的ConcurrentHashMap管理任务、自动注册处理器实现类等。
一、实体类
package com.gkfx.farm.model.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.time.LocalDateTime;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
/**
* 定时任务配置表
* @TableName sys_job
*/
@TableName(value ="sys_job")
@Data
@Schema(description = "定时任务配置表",name = "定时任务配置表")
public class SysJob extends BaseEntity {
/**
* 任务名称
*/
@Length(max = 64,message = "任务名称长度不能超过64个字符")
@NotBlank(message = "任务名称不能为空")
@Schema(description = "jobName",name = "任务名称")
@TableField("job_name")
private String jobName;
/**
* Cron表达式
*/
@Length(max = 64,message = "Cron表达式长度不能超过64个字符")
@NotBlank(message = "Cron表达式不能为空")
@Schema(description = "cronExpression",name = "Cron表达式")
@TableField("cron_expression")
private String cronExpression;
/**
* 状态 0-启用 1-停用
*/
@Schema(description = "status",name = "状态 0-启用 0-停用")
@TableField("status")
private Integer status;
/**
*
*/
@Schema(description = "description",name = "备注")
private String description;
/**
* 文件路径
*/
@Length(max = 255,message = "文件路径长度不能超过255个字符")
@Schema(description = "filePath",name = "文件路径")
@TableField("file_path")
private String filePath;
/**
*
*/
@Length(max = 64,message = "执行周期长度不能超过64个字符")
@Schema(description = "execCycle",name = "execCycle")
@TableField("exec_cycle")
private String execCycle;
/**
* 执行参数
*/
@Schema(description = "execParams",name = "执行参数")
@TableField("exec_params")
private String execParams;
/**
*
*/
@Length(max = 255,message = "备注长度不能超过255个字符")
@Schema(description = "remark",name = "remark")
@TableField("remark")
private String remark;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}
二、controller
package com.gkfx.farm.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.gkfx.farm.config.DynamicTaskConfig;
import com.gkfx.farm.mapper.SysJobMapper;
import com.gkfx.farm.model.pojo.Result;
import com.gkfx.farm.model.pojo.SysJob;
import com.gkfx.farm.service.SysJobService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.Date;
@Tag(name = "任务调度")
@RestController
@RequestMapping("/job")
public class JobController extends BaseController {
@Autowired
private SysJobService sysJobService;
@Autowired
private SysJobMapper taskMapper;
@Autowired
private DynamicTaskConfig dynamicTaskConfig;
/**
* 查询任务列表
*/
@Operation(description = "查询任务列表")
@PostMapping("/list")
@PreAuthorize("hasAuthority('job.query')")
public Result listJobs(Page page,
@RequestBody SysJob sysjob) {
LambdaQueryWrapper<SysJob> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.like(StringUtils.isNotEmpty(sysjob.getJobName()), SysJob::getJobName, sysjob.getJobName())
.eq(sysjob.getStatus() != null, SysJob::getStatus, sysjob.getStatus())
.orderByDesc(SysJob::getCreateTime);
return Result.ok(sysJobService.page(page, queryWrapper));
}
// 新增任务
@Operation(description = "新增任务")
@PostMapping("/add")
@PreAuthorize("hasAuthority('job.edit')")
public Result addTask(@Valid @RequestBody SysJob task) {
task.setUpdateTime(new Date());
task.setCreateUser(super.getUserName());
task.setStatus(0);
dynamicTaskConfig.addTask(task);
return Result.ok();
}
// 手动执行任务
@Operation(description = "手动执行任务")
@GetMapping("/execute/{taskId}")
@PreAuthorize("hasAuthority('job.edit')")
public Result executeTask(@PathVariable Long taskId) {
SysJob task = taskMapper.selectById(taskId);
if (task == null) {
return Result.fail().setCode(2).setMessage("任务不存在");
}
var handler = dynamicTaskConfig.getHandlerFactory().getHandler(task.getFilePath());
if (handler == null) {
return Result.fail().setCode(2).setMessage("无匹配处理器,无法执行");
}
task.setUpdateTime(new Date());
handler.execute(task);
taskMapper.updateById(task);
System.out.println("手动执行成功");
return Result.ok();
}
// 暂停任务
@Operation(description = "暂停任务")
@GetMapping("/pause/{taskId}")
@PreAuthorize("hasAuthority('job.edit')")
public Result pauseTask(@PathVariable Long taskId) {
SysJob task = taskMapper.selectById(taskId);
if (task == null) {
return Result.fail().setCode(2).setMessage("任务不存在");
}
// 检查任务状态
if (task.getStatus() == 1) {
return Result.fail().setCode(2).setMessage("任务已处于暂停状态");
}
// 更新任务状态
task.setStatus(1);
dynamicTaskConfig.updateTask(task);
taskMapper.updateById(task);
return Result.ok();
}
// 恢复任务
@Operation(description = "恢复任务")
@GetMapping("/resume/{taskId}")
@PreAuthorize("hasAuthority('job.edit')")
public Result resumeTask(@PathVariable Long taskId) {
SysJob task = taskMapper.selectById(taskId);
if (task == null) {
return Result.fail().setCode(2).setMessage("任务不存在");
}
// 检查任务状态
if (task.getStatus() == 0) {
return Result.fail().setCode(2).setMessage("任务已处于运行状态");
}
// 更新任务状态
task.setStatus(0);
dynamicTaskConfig.updateTask(task);
taskMapper.updateById(task);
return Result.ok();
}
}
三、config
package com.gkfx.farm.config;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.gkfx.farm.handler.TaskHandler;
import com.gkfx.farm.handler.TaskHandlerFactory;
import com.gkfx.farm.mapper.SysJobMapper;
import com.gkfx.farm.model.pojo.SysJob;
import jakarta.annotation.PostConstruct;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
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;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
@Configuration
@EnableScheduling
public class DynamicTaskConfig implements SchedulingConfigurer {
private final TaskScheduler taskScheduler;
private final SysJobMapper taskMapper;
private final TaskHandlerFactory handlerFactory;
private final Map<Long, ScheduledFuture<?>> scheduledTasks = new ConcurrentHashMap<>();
public DynamicTaskConfig(TaskScheduler taskScheduler,
SysJobMapper taskMapper,
TaskHandlerFactory handlerFactory) {
this.taskScheduler = taskScheduler;
this.taskMapper = taskMapper;
this.handlerFactory = handlerFactory;
}
@PostConstruct
public void initTasks() {
// 应用启动时加载启用的任务
LambdaQueryWrapper<SysJob> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SysJob::getStatus, 0);
List<SysJob> tasks = taskMapper.selectList(queryWrapper);
tasks.forEach(this::registerTask);
}
private void registerTask(SysJob task) {
TaskHandler handler = handlerFactory.getHandler(task.getFilePath());
if (handler == null) {
System.err.printf("任务【%s】无匹配处理器,跳过注册!\n", task.getFilePath());
return;
}
Runnable taskRunnable = () -> {
handler.execute(task);
task.setUpdateTime(new Date());
taskMapper.updateById(task); // 执行后更新任务(如最后执行时间)
};
CronTrigger trigger = new CronTrigger(task.getCronExpression());
ScheduledFuture<?> future = taskScheduler.schedule(taskRunnable, trigger);
scheduledTasks.put(task.getId(), future);
}
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setTaskScheduler(taskScheduler);
}
// 新增任务(供 Controller 调用)
public void addTask(SysJob task) {
taskMapper.insert(task);
if (task.getStatus() == 0) {
registerTask(task);
}
}
// 更新任务(供 Controller 调用)
public void updateTask(SysJob task) {
taskMapper.updateById(task);
ScheduledFuture<?> future = scheduledTasks.remove(task.getId());
if (future != null) {
future.cancel(true);
}
if (task.getStatus() == 0) {
registerTask(task);
}
}
public TaskHandlerFactory getHandlerFactory() {
return handlerFactory;
}
}
五、TaskHandlerFactory
package com.gkfx.farm.handler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component
public class TaskHandlerFactory {
private final Map<String, TaskHandler> handlerMap = new ConcurrentHashMap<>();
@Autowired
public TaskHandlerFactory(Map<String, TaskHandler> handlers) {
// 自动注入所有 TaskHandler 实现,这里简单用 Bean 名称映射(可自定义规则)
handlers.forEach((beanName, handler) -> handlerMap.put(beanName, handler));
}
public TaskHandler getHandler(String filePath) {
return handlerMap.get(filePath);
}
}
🌟感谢支持 听忆.-CSDN博客
| 🎈众口难调🎈从心就好 |
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)