🎈边走、边悟🎈迟早会好

本文展示了一个基于Spring Boot的动态任务调度系统实现,包含核心代码片段。系统采用MyBatis-Plus进行数据持久化,通过ScheduledTaskRegistrar实现动态任务管理,支持以下功能:

  1. 任务实体类定义(含cron表达式、状态等字段)
  2. 任务CRUD及状态管理接口(启停/手动执行)
  3. 动态任务配置(启动时加载+运行时注册)
  4. 处理器工厂模式(TaskHandlerFactory)实现不同任务类型的执行逻辑
  5. 基于权限控制的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博客

🎈众口难调🎈从心就好

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐