动态定时任务与动态生成class代码

动态定时任务

原理

采用定时任务线程池ThreadPoolTaskScheduler来实现定时任务。动态定时任务就是可以配置的,而不是写死在代码中。所以我们要将其写入到数据库中,然后暴露接口就可以进行配置比如创建、启动、结束任务。

数据库脚本


DROP TABLE IF EXISTS `tb_task`;
CREATE TABLE `tb_task`  (
  `task_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '定时任务id',
  `task_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '定时任务名称',
  `task_desc` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '定时任务描述',
  `task_exp` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '定时任务Cron表达式',
  `task_status` int(0) NULL DEFAULT NULL COMMENT '定时任务状态,0停用 1启用',
  `task_class` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '定时任务的Runnable任务类完整路径',
  `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
  PRIMARY KEY (`task_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '动态定时任务表' ROW_FORMAT = Compact;

INSERT INTO `tb_task` VALUES ('1', '测试打印helloworld', '这是一个描述', '0/10 * * * * ? ', 0, 'com.example.demo.Task.HelloTask', '2022-05-16 09:59:03', NULL);
INSERT INTO `tb_task` VALUES ('2', '这是一个含参数的task', '这是一个描述', '0/10 * * * * ? ', 0, 'com.example.demo.Task.parameterTask', '2022-05-16 10:41:10', NULL);

实体类

package com.example.demo.entity;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;
import java.util.Date;

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

/**
 * <p>
 * 动态定时任务表
 * </p>
 *
 * @author author
 * @since 2022-05-13
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("tb_task")
public class TbTask implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 定时任务id
     */
    @TableId(value = "task_id", type = IdType.NONE)
    private String taskId;

    /**
     * 定时任务名称
     */
    @TableField("task_name")
    private String taskName;

    /**
     * 定时任务描述
     */
    @TableField("task_desc")
    private String taskDesc;

    /**
     * 定时任务Cron表达式
     */
    @TableField("task_exp")
    private String taskExp;

    /**
     * 定时任务状态,0停用 1启用
     */
    @TableField("task_status")
    private Integer taskStatus;

    /**
     * 定时任务的Runnable任务类完整路径
     */
    @TableField("task_class")
    private String taskClass;

    /**
     * 更新时间
     */
    @TableField("update_time")
    private Date updateTime;

    /**
     * 创建时间
     */
    @TableField("create_time")
    private Date createTime;


}

定时任务实现类

package com.example.demo.component;

import com.example.demo.entity.TbTask;
import com.example.demo.mapper.TbTaskMapper;
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.Component;

import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;

@Slf4j
@Component
public class TaskScheduler {

    //数据库的任务
    public static ConcurrentHashMap<String, TbTask> tasks = new ConcurrentHashMap<>(10);

    //正在运行的任务
    public static ConcurrentHashMap<String, ScheduledFuture> runTasks = new ConcurrentHashMap<>(10);

    //线程池任务调度
    private final ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();

    @Autowired
    private TbTaskMapper tbTaskMapper;

    /**
     * 初始化线程池任务调度
     * 参数可由配置文件导入
     */
    @Autowired
    public TaskScheduler() {
        this.threadPoolTaskScheduler.setPoolSize(10);
        this.threadPoolTaskScheduler.setThreadNamePrefix("task-thread-");
        this.threadPoolTaskScheduler.setWaitForTasksToCompleteOnShutdown(true);
        this.threadPoolTaskScheduler.initialize();
    }


    /**
     * 获取所有数据库里的定时任务
     */
    public void getAllTbTask() {
        //查询所有,并put到tasks
        TaskScheduler.tasks.clear();
        List<TbTask> list = tbTaskMapper.selectList(null);
        list.forEach((task) -> TaskScheduler.tasks.put(task.getTaskId(), task));
    }

    /**
     * 根据定时任务id,启动定时任务
     */
    public void start(String taskId) {
        try {
            //如果为空,重新获取
            if (TaskScheduler.tasks.size() <= 0) {
                this.getAllTbTask();
            }
            TbTask tbTask = TaskScheduler.tasks.get(taskId);
            if (tbTask == null) {
                tbTask = tbTaskMapper.selectById(taskId);
            }
            if (tbTask == null)
                throw new RuntimeException("task不存在");

            //获取并实例化Runnable任务类
            Class<?> clazz = Class.forName(tbTask.getTaskClass());
            Runnable runnable = (Runnable) clazz.newInstance();

            //Cron表达式
            CronTrigger cron = new CronTrigger(tbTask.getTaskExp());

            //执行,并put到runTasks
            TaskScheduler.runTasks.put(taskId, Objects.requireNonNull(this.threadPoolTaskScheduler.schedule(runnable, cron)));

            this.updateTaskStatus(taskId, 1);

            log.info("{},任务启动!", taskId);
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
            log.error("{},任务启动失败...", taskId);
            e.printStackTrace();
        }

    }

    public void start(String taskId,Runnable runnable) {
        try {
            //如果为空,重新获取
            if (TaskScheduler.tasks.size() <= 0) {
                this.getAllTbTask();
            }
            TbTask tbTask = TaskScheduler.tasks.get(taskId);
            if (tbTask == null) {
                tbTask = tbTaskMapper.selectById(taskId);
            }
            if (tbTask == null)
                throw new RuntimeException("task不存在");
            //Cron表达式
            CronTrigger cron = new CronTrigger(tbTask.getTaskExp());

            //执行,并put到runTasks
            TaskScheduler.runTasks.put(taskId, Objects.requireNonNull(this.threadPoolTaskScheduler.schedule(runnable, cron)));

            this.updateTaskStatus(taskId, 1);

            log.info("{},任务启动!", taskId);
        } catch (RuntimeException e) {
            log.error("{},任务启动失败...", taskId);
            e.printStackTrace();
        }

    }

    /**
     * 根据定时任务id,停止定时任务
     */
    public void stop(String taskId) {
        // 停止任务
        TaskScheduler.runTasks.get(taskId).cancel(true);

        TaskScheduler.runTasks.remove(taskId);

        this.updateTaskStatus(taskId, 0);

        log.info("{},任务停止...", taskId);
    }

    /**
     * 更新数据库动态定时任务状态
     */
    public void updateTaskStatus(String taskId, int status) {
        TbTask task = tbTaskMapper.selectById(taskId);
        task.setTaskStatus(status);
        task.setUpdateTime(new Date());
        tbTaskMapper.updateById(task);
    }

}

controller以及postman调用

由于只是demo,有些未完善。

package com.example.demo.controller;

import com.example.demo.Task.parameterTask;
import com.example.demo.component.TaskScheduler;
import com.example.demo.component.classGenerate;
import com.example.demo.entity.TbTask;
import com.example.demo.entity.VO.ResultVo;
import com.example.demo.service.ITbTaskService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;

@RestController
@RequestMapping("/demo/task")
public class TaskController {

    @Autowired
    TaskScheduler taskScheduler;

    @Autowired
    ITbTaskService tbTaskService;

    /**
     * 添加并启动任务
     * @param tbTask
     * @return
     */
    @PostMapping
    public ResultVo addTask(@RequestBody TbTask tbTask){
        tbTaskService.save(tbTask);
        String taskId = tbTask.getTaskId();
        taskScheduler.start(taskId);
        return ResultVo.SUCCESS("添加并启动成功").data("taskId",taskId);
    }

    /**
     * 添加并启动任务,含参任务
     * @param tbTask
     * @param name
     * @return
     */
    @PostMapping("/{name}")
    public ResultVo addTask2(@RequestBody TbTask tbTask,@PathVariable("name") String name){
        tbTaskService.save(tbTask);
        String taskId = tbTask.getTaskId();
        parameterTask parameterTask = new parameterTask();
        parameterTask.setName(name);
        taskScheduler.start(taskId,parameterTask);
        return ResultVo.SUCCESS("添加并启动成功").data("taskId",taskId);
    }

    /**
     * 启动任务
     * @param taskId
     * @return
     */
    @GetMapping("/start/{taskId}")
    public ResultVo start(@PathVariable("taskId") String taskId){
        taskScheduler.start(taskId);
        return ResultVo.SUCCESS("启动成功").data("taskId",taskId);
    }

    /**
     * 停止任务
     * @param taskId
     * @return
     */
    @GetMapping("/stop/{taskId}")
    public ResultVo stop(@PathVariable("taskId") String taskId){
        taskScheduler.stop(taskId);
        return ResultVo.SUCCESS("停止任务").data("taskId",taskId);
    }

    @PostMapping("/generate")
    public ResultVo generateJava(@RequestBody HashMap<String,String> map) throws Exception {
        String path = map.get("path");
        String javaCode = map.get("javaCode");
        classGenerate.createStudentByFile(path,javaCode);
        return ResultVo.SUCCESS().message("代码生成成功");
    }
}

下面是postman测试
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

动态生成class

实现代码

package com.example.demo.component;

import org.springframework.stereotype.Component;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;

/**
 * Class生成
 */
public class classGenerate {
    /**
     * 生成java文件并加载class
     * @param path /com/example/demo/xxx/xxx.java 类生成路径
     * @param JavaCode 类代码
     * @throws Exception
     */
    public static void createStudentByFile(String path,String JavaCode) throws Exception{
//        String fileName = System.getProperty("user.dir") + "/src/main/java"+path;
//        String fileName = "E:/data/JavaCode"+path;
//        String fileName = "/data/JavaCode"+path;
        // path = /com/xx/xxx/task/xxx.java
        String fileName = path;
        logger.info("fileName:[{}]",fileName);
        File file = new File(fileName);
        if(!file.exists()){
            file.getParentFile().mkdirs();
        }
        FileWriter fileWriter = new FileWriter(file);
        fileWriter.write(JavaCode);
        fileWriter.flush();
        fileWriter.close();
        // xxx/classes/
        URL location = DemoApplication.class.getProtectionDomain().getCodeSource().getLocation();
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager manager = compiler.getStandardFileManager(null,null,null);
        Iterable<? extends JavaFileObject> javaFileObjects = manager.getJavaFileObjects(fileName);

        String dest = location.getPath();
        logger.info("dest: [ {} ]",dest);
        //options就是指定编译输入目录,与我们命令行写javac -d C://是一样的
        List<String> options = new ArrayList<String>();
        options.add("-d");
        options.add(dest);
        JavaCompiler.CompilationTask task = compiler.getTask(null,manager,null,options,null,javaFileObjects);
        task.call();
        manager.close();
//        URL[] urls = new URL[]{new URL("file:/" + System.getProperty("user.dir") + "/target/classes")};

        //加载class时要告诉你的classloader去哪个位置加载class文件

//        ClassLoader classLoader = new URLClassLoader(urls);
//        Object student = classLoader.loadClass("com.example.demo.Task.HelloTask2").newInstance();
    }
    
    
}

测试

 @PostMapping("/generate")
    public ResultVo generateJava(@RequestBody HashMap<String,String> map) throws Exception {
        String path = map.get("path");
        String javaCode = map.get("javaCode");
        classGenerate.createStudentByFile(path,javaCode);
        return ResultVo.SUCCESS().message("代码生成成功");
    }

postman
由于是传入代码,所以遇到换行要使用\n""转义使用\",否则会报错,在postman测试中json参数需要在一行中。
在这里插入图片描述

注意:原先我上述代码中使用了@Slf4j,但是这样的话class编译的时候会报错,没办法找到。可能是lombok的插件的原因吧。此外编译后,会有一些警告,但是不影响编译完成。

警告报错如下:
在这里插入图片描述

E:\project\hello_world\src\main\java\com\example\demo\Task\HelloTask2.java:4: 警告: Can't initialize javac processor due to (most likely) a class loader problem: java.lang.NoClassDefFoundError: com/sun/tools/javac/processing/JavacProcessingEnvironment
public class HelloTask2 implements Runnable{
       ^
  	at lombok.javac.apt.LombokProcessor.init(LombokProcessor.java:85)
  	at lombok.core.AnnotationProcessor$JavacDescriptor.want(AnnotationProcessor.java:87)
  	at lombok.core.AnnotationProcessor.init(AnnotationProcessor.java:140)
  	at lombok.launch.AnnotationProcessorHider$AnnotationProcessor.init(AnnotationProcessor.java:69)
  	at com.sun.tools.javac.processing.JavacProcessingEnvironment$ProcessorState.<init>(JavacProcessingEnvironment.java:500)
  	at com.sun.tools.javac.processing.JavacProcessingEnvironment$DiscoveredProcessors$ProcessorStateIterator.next(JavacProcessingEnvironment.java:597)
  	at com.sun.tools.javac.processing.JavacProcessingEnvironment.discoverAndRunProcs(JavacProcessingEnvironment.java:690)
  	at com.sun.tools.javac.processing.JavacProcessingEnvironment.access$1800(JavacProcessingEnvironment.java:91)
  	at com.sun.tools.javac.processing.JavacProcessingEnvironment$Round.run(JavacProcessingEnvironment.java:1035)
  	at com.sun.tools.javac.processing.JavacProcessingEnvironment.doProcessing(JavacProcessingEnvironment.java:1176)
  	at com.sun.tools.javac.main.JavaCompiler.processAnnotations(JavaCompiler.java:1170)
  	at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:856)
  	at com.sun.tools.javac.main.Main.compile(Main.java:523)
  	at com.sun.tools.javac.api.JavacTaskImpl.doCall(JavacTaskImpl.java:129)
  	at com.sun.tools.javac.api.JavacTaskImpl.call(JavacTaskImpl.java:138)
  	at com.example.demo.component.classGenerate.createStudentByFile(classGenerate.java:47)
  	at com.example.demo.controller.TaskController.generateJava(TaskController.java:79)
  	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  	at java.lang.reflect.Method.invoke(Method.java:498)
  	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:209)
  	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)
  	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
  	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:877)
  	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:783)
  	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
  	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991)
  	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)
  	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974)
  	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:877)
  	at javax.servlet.http.HttpServlet.service(HttpServlet.java:661)
  	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851)
  	at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
  	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
  	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
  	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
  	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
  	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
  	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
  	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
  	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
  	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
  	at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109)
  	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
  	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
  	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
  	at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81)
  	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
  	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
  	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
  	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
  	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
  	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
  	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
  	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
  	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
  	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496)
  	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
  	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
  	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
  	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
  	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803)
  	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
  	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790)
  	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459)
  	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
  	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
  	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
  	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
  	at java.lang.Thread.run(Thread.java:748)
  Caused by: java.lang.ClassNotFoundException: com.sun.tools.javac.processing.JavacProcessingEnvironment
  	at java.lang.ClassLoader.findClass(ClassLoader.java:523)
  	at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
  	at lombok.launch.ShadowClassLoader.loadClass(ShadowClassLoader.java:422)
  	at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
  	... 71 more
1 个警告

但是这个警告不影响!!
解决办法:
在SDKs中添加tools.jar ,路径一般在jdk/lib/ 下面
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

与动态定时任务结合

由于我们前面创建的是一个Runnable对象,所以我们可以对他进行创建定时任务
在这里插入图片描述

github地址

仓库链接

部署问题

由于是jar包部署,在生成Java文件和class文件时,无法加入到jar包中。对于这个问题,我采用解压jar包的方式运行。

image.png

具体步骤

# 解压到当前目录 会在当前目录生成 BOOT-INF、org、META-INF
jar -xf app.jar
#  指定入口  : 为分隔符 
# java -cp BOOT-INF/classes:BOOT-INF/lib/*  com.example.demo.DemoApplication
# 部署
nohup java -cp BOOT-INF/classes:BOOT-INF/lib/* com.example.demo.DemoApplication > nohup.log &

java org.springframework.boot.loader.JarLauncher 也能启动项目,但是无法生成class文件!

这样的话,class文件就可以写入到 解压后的jar的BOOT-INF/classes/路径下。

测试

创建生成代码

image.png

在jar包所在目录会生成上述传入的path结构的java代码。

image.png

image.png

并且在 BOOT-INF/classes也会生成对应的class文件。

image.png

启动任务

image.png

image.png

class文件生成的目录依赖于传入的Java代码中的 package com.example.demo.Task;,这样生成的class就会在 BOOT-INF/classes/com/example/demo/Task 目录下啦!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用 Spring Boot 中的定时任务框架 `@Scheduled` 来实现动态定时任务。具体步骤如下: 1. 创建一个实体类,用于存储定时任务的相关信息,如任务名、Cron表达式等。 2. 创建一个数据库表,用于存储定时任务的相关信息。 3. 在 Spring Boot 中创建一个定时任务管理器,用于启动、停止和删除定时任务。 4. 使用 Spring Data JPA 或 MyBatis 等持久化框架,从数据库中读取定时任务的相关信息,并使用 `@Scheduled` 注解创建对应的定时任务。 5. 提供接口,允许用户通过 API 的方式添加、修改或删除定时任务。 下面是一个简单的示例: 1. 定义实体类: ```java @Entity public class Task { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String cron; // 省略Getter和Setter } ``` 2. 创建数据库表: ```sql CREATE TABLE `task` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, `cron` varchar(255) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4; ``` 3. 创建定时任务管理器: ```java @Component public class TaskScheduler { private static final Logger logger = LoggerFactory.getLogger(TaskScheduler.class); private final ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); public TaskScheduler() { scheduler.setThreadNamePrefix("task-thread-"); scheduler.setPoolSize(10); scheduler.initialize(); } public void scheduleTask(Task task) { logger.info("Schedule task: {}", task.getName()); scheduler.schedule(() -> { // TODO: 执行定时任务代码 logger.info("Task executed: {}", task.getName()); }, new CronTrigger(task.getCron())); } public void cancelTask(Task task) { logger.info("Cancel task: {}", task.getName()); scheduler.getScheduledThreadPoolExecutor().getQueue().stream() .filter(taskScheduledFuture -> { Object taskToRun = taskScheduledFuture.getTask(); return taskToRun instanceof ScheduledMethodRunnable && ((ScheduledMethodRunnable) taskToRun).getMethod().getName().equals(task.getName()); }) .findFirst() .ifPresent(taskScheduledFuture -> taskScheduledFuture.cancel(true)); } } ``` 4. 使用 `@Scheduled` 注解创建定时任务: ```java @Service public class TaskService { private final TaskRepository taskRepository; private final TaskScheduler taskScheduler; public TaskService(TaskRepository taskRepository, TaskScheduler taskScheduler) { this.taskRepository = taskRepository; this.taskScheduler = taskScheduler; } @Scheduled(fixedRate = 5000) public void pollTasks() { List<Task> tasks = taskRepository.findAll(); for (Task task : tasks) { taskScheduler.scheduleTask(task); } } } ``` 5. 提供接口,允许用户操作定时任务。 参考资料: - [Spring Boot 定时任务的实现](https://www.jianshu.com/p/ce4784bb863c) - [Spring Boot 定时任务详解](https://www.jianshu.com/p/9f6e385a4a2e)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值