提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
最近安排了实习生实现动态定时任务,让参考网上一些资料,不过对于初学者理解都有些难度。为了初学者更好的接受和理解,写了一个精简版实现。
先看一个不踩坑的官方文档:scheduling。
建议看完后有自己的理解再去看其他实现资料,就通透很多。
比较简单,官方文档有具体实现和描述:参考文档7.3部分
最基本springboot项目为例,完整代码结构如下:
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>
启动类代码如下:
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;
}
}
包括了任务调度线程池的配置。
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);
}
}
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;
}
}
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);
}
}
项目运行日志:
打开swagger:http://127.0.0.1:8080/swagger-ui/,选择接口测试
例如查询运行中的定时任务:
以上是项目的所有文件,只完成了最基本实现,并未完全按照项目规范拆分和调用,仅作为实现参考。