spring boot动态定时任务的简单实现

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、关于Spring Scheduling定时任务
  • 二、基于@Scheduled注解静态定时任务
  • 三、动态定时任务简单实现
    • 1.创建spring boot项目
    • 2.启动类和配置类
    • 3.自定义任务实体类
    • 4.业务逻辑
    • 5.启动初始化和控制器
    • 6.运行结果
  • 结语


前言

最近安排了实习生实现动态定时任务,让参考网上一些资料,不过对于初学者理解都有些难度。为了初学者更好的接受和理解,写了一个精简版实现。


一、关于Spring Scheduling定时任务

先看一个不踩坑的官方文档:scheduling。
建议看完后有自己的理解再去看其他实现资料,就通透很多。

二、基于@Scheduled注解静态定时任务

比较简单,官方文档有具体实现和描述:参考文档7.3部分

三、动态定时任务简单实现

1.创建spring boot项目

最基本springboot项目为例,完整代码结构如下:
spring boot动态定时任务的简单实现_第1张图片
pom文件依赖如下:

	<dependencies>
        
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <scope>providedscope>
            <optional>trueoptional>
        dependency>

        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>

        
        <dependency>
            <groupId>io.springfoxgroupId>
            <artifactId>springfox-boot-starterartifactId>
            <version>RELEASEversion>
        dependency>

        
        <dependency>
            <groupId>org.apache.commonsgroupId>
            <artifactId>commons-lang3artifactId>
            <version>RELEASEversion>
        dependency>
    dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-dependenciesartifactId>
                <version>2.5.12version>
                <type>pomtype>
                <scope>importscope>
            dependency>
        dependencies>
    dependencyManagement>

2.启动类和配置类

启动类代码如下:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

@SpringBootApplication
public class TaskApp {

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

    @Bean
    public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
        ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
        threadPoolTaskScheduler.setPoolSize(20);
        threadPoolTaskScheduler.setThreadNamePrefix("taskExecutor-");
        threadPoolTaskScheduler.setWaitForTasksToCompleteOnShutdown(true);
        threadPoolTaskScheduler.setAwaitTerminationSeconds(60);
        return threadPoolTaskScheduler;
    }
}

包括了任务调度线程池的配置。

3.自定义任务实体类

MyTask类代码如下:

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.time.LocalDateTime;

@Slf4j
@Setter
@Getter
public class MyTask implements Runnable {

    private String taskId;

    private String cron;

    public MyTask(String taskId, String cron) {
        this.taskId = taskId;
        this.cron = cron;
    }

    @Override
    public void run() {
        log.info("task run success! time: {} ; taskId: {}", LocalDateTime.now(), taskId);
    }

}

4.业务逻辑

TaskService类代码如下:

import org.apache.commons.lang3.RandomUtils;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
public class TaskService {

    // 测试任务池,实际使用中为数据表操作
    private static final Map<String, MyTask> taskMap = getTask(RandomUtils.nextInt(5, 30));
    // 定时器存储
    private final Map<String, ScheduledFuture<?>> scheduledFutureMap = new ConcurrentHashMap<>();
    @Resource
    private ThreadPoolTaskScheduler threadPoolTaskScheduler;

    /**
     * 测试数据,代替数据库数据
     *
     * @param count 生产的数据量
     * @return 实际中对应数据库返回的任务配置
     */
    private static Map<String, MyTask> getTask(Integer count) {
        return Stream.generate(() -> new MyTask("my-task-" + RandomUtils.nextInt(),
                MessageFormat.format("{0} */{1} * * * ?", RandomUtils.nextInt(1, 59), RandomUtils.nextInt(1, 10))))
                .limit(count)
                .collect(Collectors.toMap(MyTask::getTaskId, Function.identity()));
    }

    /**
     * 获取运行中的任务
     *
     * @return 任务列表
     */
    public List<MyTask> getRunningTasks() {
        return scheduledFutureMap.keySet().stream().map(taskMap::get).collect(Collectors.toList());
    }

    /**
     * 初始化定时任务
     *
     * @return 初始化条数
     */
    public int initTasks() {
        taskMap.values().forEach(this::start);
        return taskMap.size();
    }

    /**
     * 开始任务
     *
     * @param taskId 任务id
     */
    public boolean startTask(String taskId) {
        MyTask myTask = taskMap.get(taskId);
        return myTask != null && start(myTask);
    }

    /**
     * 结束任务
     *
     * @param taskId 任务id
     */
    public boolean stopTask(String taskId) {
        return stop(taskId);
    }

    /**
     * 重启任务
     *
     * @param taskId 任务id
     */
    public boolean restartTask(String taskId) {
        return stopTask(taskId) && startTask(taskId);
    }

    /**
     * 添加任务
     *
     * @param taskId 任务id
     * @param cron   cron表达式
     */
    public Boolean addTask(String taskId, String cron) {
        MyTask myTask = new MyTask(taskId, cron);
        taskMap.put(taskId, myTask);
        return start(myTask);
    }

    /**
     * 修改任务
     *
     * @param taskId 任务id
     * @param cron   cron表达式
     */
    public boolean setTask(String taskId, String cron) {
        MyTask myTask = taskMap.get(taskId);
        myTask.setCron(cron);
        return restartTask(taskId);
    }

    private boolean start(MyTask task) {
        ScheduledFuture<?> scheduledFuture = threadPoolTaskScheduler.schedule(task, new CronTrigger(task.getCron()));
        scheduledFutureMap.put(task.getTaskId(), scheduledFuture);
        return true;
    }

    private boolean stop(String taskId) {
        ScheduledFuture<?> scheduledFuture = scheduledFutureMap.get(taskId);
        return scheduledFuture.cancel(false) && scheduledFutureMap.remove(taskId) != null;
    }

}

5.启动初始化和控制器

TaskRunner类代码如下:

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

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

@Slf4j
@RestController
@RequestMapping("/task")
public class TaskRunner implements ApplicationRunner {

    @Resource
    private TaskService taskService;

    @Override
    public void run(ApplicationArguments applicationArguments) {
        log.info(" >>>>>> 项目启动完毕, 开启定时任务!");
        int count = taskService.initTasks();
        log.info(" >>>>>> 开启 {} 条定时任务!", count);
    }

    @GetMapping("/getTasks")
    public List<MyTask> getTasks() {
        return taskService.getRunningTasks();
    }

    @GetMapping("/start/{taskId}")
    public Boolean start(@PathVariable String taskId) {
        return taskService.startTask(taskId);
    }

    @GetMapping("/stop/{taskId}")
    public Boolean stop(@PathVariable String taskId) {
        return taskService.stopTask(taskId);
    }

    @GetMapping("/restart/{taskId}")
    public Boolean restart(@PathVariable String taskId) {
        return taskService.restartTask(taskId);
    }

    @PostMapping("/add/{taskId}")
    public Boolean add(@PathVariable String taskId, @RequestBody String cron) {
        return taskService.addTask(taskId, cron);
    }

    @PostMapping("/set/{taskId}")
    public Boolean set(@PathVariable String taskId, @RequestBody String cron) {
        return taskService.setTask(taskId, cron);
    }
}

6.运行结果

项目运行日志:
spring boot动态定时任务的简单实现_第2张图片
打开swagger:http://127.0.0.1:8080/swagger-ui/,选择接口测试
spring boot动态定时任务的简单实现_第3张图片
例如查询运行中的定时任务:
spring boot动态定时任务的简单实现_第4张图片


结语

以上是项目的所有文件,只完成了最基本实现,并未完全按照项目规范拆分和调用,仅作为实现参考。

你可能感兴趣的:(springboot,java,spring,boot,java)