SpringBoot 实现动态添加定时任务功能

作者:lijiahangmax 时间:2022-07-12 05:07:40 

最近的需求有一个自动发布的功能, 需要做到每次提交都要动态的添加一个定时任务

SpringBoot 实现动态添加定时任务功能

代码结构

SpringBoot 实现动态添加定时任务功能

1. 配置类

package com.orion.ops.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
/**
* 调度器配置
*
* @author Jiahang Li
* @version 1.0.0
* @since 2022/2/14 9:51
*/
@EnableScheduling
@Configuration
public class SchedulerConfig {
   @Bean
   public TaskScheduler taskScheduler() {
       ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
       scheduler.setPoolSize(4);
       scheduler.setRemoveOnCancelPolicy(true);
       scheduler.setThreadNamePrefix("scheduling-task-");
       return scheduler;
   }
}

2. 定时任务类型枚举

package com.orion.ops.handler.scheduler;

import com.orion.ops.consts.Const;
import com.orion.ops.handler.scheduler.impl.ReleaseTaskImpl;
import com.orion.ops.handler.scheduler.impl.SchedulerTaskImpl;
import lombok.AllArgsConstructor;
import java.util.function.Function;
/**
* 任务类型
*
* @author Jiahang Li
* @version 1.0.0
* @since 2022/2/14 10:16
*/
@AllArgsConstructor
public enum TaskType {
   /**
    * 发布任务
    */
   RELEASE(id -> new ReleaseTaskImpl((Long) id)) {
       @Override
       public String getKey(Object params) {
           return Const.RELEASE + "-" + params;
       }
   },
    * 调度任务
   SCHEDULER_TASK(id -> new SchedulerTaskImpl((Long) id)) {
           return Const.TASK + "-" + params;
   ;
   private final Function<Object, Runnable> factory;
    * 创建任务
    *
    * @param params params
    * @return task
   public Runnable create(Object params) {
       return factory.apply(params);
   }
    * 获取 key
    * @return key
   public abstract String getKey(Object params);
}

这个枚举的作用是生成定时任务的 runnable 和 定时任务的唯一值, 方便后续维护

3. 实际执行任务实现类

package com.orion.ops.handler.scheduler.impl;

import com.orion.ops.service.api.ApplicationReleaseService;
import com.orion.spring.SpringHolder;
import lombok.extern.slf4j.Slf4j;
/**
* 发布任务实现
*
* @author Jiahang Li
* @version 1.0.0
* @since 2022/2/14 10:25
*/
@Slf4j
public class ReleaseTaskImpl implements Runnable {
   protected static ApplicationReleaseService applicationReleaseService = SpringHolder.getBean(ApplicationReleaseService.class);
   private Long releaseId;
   public ReleaseTaskImpl(Long releaseId) {
       this.releaseId = releaseId;
   }
   @Override
   public void run() {
       log.info("定时执行发布任务-触发 releaseId: {}", releaseId);
       applicationReleaseService.runnableAppRelease(releaseId, true);
}

4. 定时任务包装器

package com.orion.ops.handler.scheduler;

import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.Trigger;
import java.util.Date;
import java.util.concurrent.ScheduledFuture;
/**
* 定时 任务包装器
*
* @author Jiahang Li
* @version 1.0.0
* @since 2022/2/14 10:34
*/
public class TimedTask {
   /**
    * 任务
    */
   private Runnable runnable;
    * 异步执行
   private volatile ScheduledFuture<?> future;
   public TimedTask(Runnable runnable) {
       this.runnable = runnable;
   }
    * 提交任务 一次性
    *
    * @param scheduler scheduler
    * @param time      time
   public void submit(TaskScheduler scheduler, Date time) {
       this.future = scheduler.schedule(runnable, time);
    * 提交任务 cron表达式
    * @param trigger   trigger
   public void submit(TaskScheduler scheduler, Trigger trigger) {
       this.future = scheduler.schedule(runnable, trigger);
    * 取消定时任务
   public void cancel() {
       if (future != null) {
           future.cancel(true);
       }
}

这个类的作用是包装实际执行任务, 以及提供调度器的执行方法

5. 任务注册器 (核心)

package com.orion.ops.handler.scheduler;

import com.orion.ops.consts.MessageConst;
import com.orion.utils.Exceptions;
import com.orion.utils.collect.Maps;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Date;
import java.util.Map;
/**
* 任务注册器
*
* @author Jiahang Li
* @version 1.0.0
* @since 2022/2/14 10:46
*/
@Component
public class TaskRegister implements DisposableBean {
   private final Map<String, TimedTask> taskMap = Maps.newCurrentHashMap();
   @Resource
   @Qualifier("taskScheduler")
   private TaskScheduler scheduler;
   /**
    * 提交任务
    *
    * @param type   type
    * @param time   time
    * @param params params
    */
   public void submit(TaskType type, Date time, Object params) {
       // 获取任务
       TimedTask timedTask = this.getTask(type, params);
       // 执行任务
       timedTask.submit(scheduler, time);
   }
    * @param cron   cron
   public void submit(TaskType type, String cron, Object params) {
       timedTask.submit(scheduler, new CronTrigger(cron));
    * 获取任务
   private TimedTask getTask(TaskType type, Object params) {
       // 生成任务
       Runnable runnable = type.create(params);
       String key = type.getKey(params);
       // 判断是否存在任务
       if (taskMap.containsKey(key)) {
           throw Exceptions.init(MessageConst.TASK_PRESENT);
       }
       TimedTask timedTask = new TimedTask(runnable);
       taskMap.put(key, timedTask);
       return timedTask;
    * 取消任务
   public void cancel(TaskType type, Object params) {
       TimedTask task = taskMap.get(key);
       if (task != null) {
           taskMap.remove(key);
           task.cancel();
    * 是否存在
   public boolean has(TaskType type, Object params) {
       return taskMap.containsKey(type.getKey(params));
   @Override
   public void destroy() {
       taskMap.values().forEach(TimedTask::cancel);
       taskMap.clear();
}

这个类提供了执行, 提交任务的api, 实现 DisposableBean 接口, 便于在bean销毁时将任务一起销毁

6. 使用

@Resource
   private TaskRegister taskRegister;

/**
    * 提交发布
    */
   @RequestMapping("/submit")
   @EventLog(EventType.SUBMIT_RELEASE)
   public Long submitAppRelease(@RequestBody ApplicationReleaseRequest request) {
       Valid.notBlank(request.getTitle());
       Valid.notNull(request.getAppId());
       Valid.notNull(request.getProfileId());
       Valid.notNull(request.getBuildId());
       Valid.notEmpty(request.getMachineIdList());
       TimedReleaseType timedReleaseType = Valid.notNull(TimedReleaseType.of(request.getTimedRelease()));
       if (TimedReleaseType.TIMED.equals(timedReleaseType)) {
           Date timedReleaseTime = Valid.notNull(request.getTimedReleaseTime());
           Valid.isTrue(timedReleaseTime.compareTo(new Date()) > 0, MessageConst.TIMED_GREATER_THAN_NOW);
       }
       // 提交
       Long id = applicationReleaseService.submitAppRelease(request);
       // 提交任务
           taskRegister.submit(TaskType.RELEASE, request.getTimedReleaseTime(), id);
       return id;
   }

最后

       这是一个简单的动态添加定时任务的工具, 有很多的改造空间, 比如想持久化可以插入到库中, 定义一个 CommandLineRunner 在启动时将定时任务全部加载, 还可以给任务加钩子自动提交,自动删除等, 代码直接cv一定会报错, 就是一些工具, 常量类会报错, 改改就好了, 本人已亲测可用, 有什么问题可以在评论区沟通

来源:https://blog.csdn.net/qq_41011894/article/details/123116277

标签:SpringBoot,定时任务
0
投稿

猜你喜欢

  • springboot+jwt+springSecurity微信小程序授权登录问题

    2022-10-13 03:36:22
  • SpringBoot面试突击之过滤器和拦截器区别详解

    2022-10-13 02:02:05
  • 关于activemq安装配置以及启动错误的解决

    2023-11-13 05:18:02
  • SpringBoot使用Spring Security实现登录注销功能

    2023-01-05 13:19:54
  • 如何使用try-with-resource机制关闭连接

    2022-04-25 01:36:54
  • Java Stream流的常见生成和操作方法总结

    2023-08-26 12:06:21
  • Mybatis与Ibatis的区别

    2022-11-19 14:27:42
  • 小菜编程成长记(一 面试受挫——代码无错就是好?)第1/3页

    2023-06-08 19:15:26
  • C# DateTime与时间戳转换实例

    2022-12-12 08:34:12
  • Android实现系统日历同步日程

    2022-02-13 09:57:44
  • java 利用反射获取内部类静态成员变量的值操作

    2023-03-28 21:30:04
  • Mybatis-Plus字段策略FieldStrategy的使用

    2023-12-13 14:44:41
  • spring boot中使用@Async实现异步调用任务

    2023-04-25 13:35:18
  • mybatis like模糊查询特殊字符报错转义处理方式

    2023-09-02 21:14:54
  • C# 时间与时间戳互转的方法(13位)

    2023-12-04 10:26:39
  • 详解Java面向对象编程之多态

    2023-08-28 19:17:06
  • Java语言中4种内部类的超详细讲解

    2022-02-23 18:08:10
  • 介绍Jersey-Jersey入门基础

    2023-11-18 06:34:26
  • spring mvc中的@PathVariable获得请求url中的动态参数

    2023-08-22 22:08:40
  • 了解Java线程池执行原理

    2022-10-12 13:00:23
  • asp之家 软件编程 m.aspxhome.com