0

0

SpringBoot动态定时任务如何实现

王林

王林

发布时间:2023-05-21 14:05:30

|

1359人浏览过

|

来源于亿速云

转载

 执行定时任务的线程池配置类

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
 
@Configuration
public class SchedulingConfig {  
    @Bean
    public TaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        // 定时任务执行线程池核心线程数  
        taskScheduler.setPoolSize(6);  
        taskScheduler.setRemoveOnCancelPolicy(true);  
        taskScheduler.setThreadNamePrefix("TaskSchedulerThreadPool-");  
        return taskScheduler;  
    }  
}

ScheduledFuture的包装类

scheduledfuture是scheduledexecutorservice定时任务线程池的执行结果。

import java.util.concurrent.ScheduledFuture;
 
public final class ScheduledTask {
  
    volatile ScheduledFuture future;
    /**  
     * 取消定时任务  
     */  
    public void cancel() {  
        ScheduledFuture future = this.future;  
        if (future != null) {  
            future.cancel(true);  
        }  
    }  
}

Runnable接口实现类

被定时任务线程池调用,用来执行指定bean里面的方法。

import com.ying.demo.utils.springContextUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import java.lang.reflect.Method;
import java.util.Objects;
 
public class SchedulingRunnable implements Runnable {
 
   private static final Logger logger = LoggerFactory.getLogger(SchedulingRunnable.class);
 
   private String beanName;
 
   private String methodName;
 
   private String params;
 
   public SchedulingRunnable(String beanName, String methodName) {
      this(beanName, methodName, null);
   }
 
   public SchedulingRunnable(String beanName, String methodName, String params) {
      this.beanName = beanName;
      this.methodName = methodName;
      this.params = params;
   }
 
   @Override
   public void run() {
      logger.info("定时任务开始执行 - bean:{},方法:{},参数:{}", beanName, methodName, params);
      long startTime = System.currentTimeMillis();
 
      try {
         Object target = springContextUtils.getBean(beanName);
 
         Method method = null;
         if (!StringUtils.isEmpty(params)) {
            method = target.getClass().getDeclaredMethod(methodName, String.class);
         } else {
            method = target.getClass().getDeclaredMethod(methodName);
         }
 
         ReflectionUtils.makeAccessible(method);
         if (!StringUtils.isEmpty(params)) {
            method.invoke(target, params);
         } else {
            method.invoke(target);
         }
      } catch (Exception ex) {
         logger.error(String.format("定时任务执行异常 - bean:%s,方法:%s,参数:%s ", beanName, methodName, params), ex);
      }
 
      long times = System.currentTimeMillis() - startTime;
      logger.info("定时任务执行结束 - bean:{},方法:{},参数:{},耗时:{} 毫秒", beanName, methodName, params, times);
   }
 
   @Override
   public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;
      SchedulingRunnable that = (SchedulingRunnable) o;
      if (params == null) {
         return beanName.equals(that.beanName) &&
               methodName.equals(that.methodName) &&
               that.params == null;
      }
 
      return beanName.equals(that.beanName) &&
            methodName.equals(that.methodName) &&
            params.equals(that.params);
   }
 
   @Override
   public int hashCode() {
      if (params == null) {
         return Objects.hash(beanName, methodName);
      }
 
      return Objects.hash(beanName, methodName, params);
   }
}

定时任务注册类

用来增加、删除定时任务

@Component
public class CronTaskRegistrar implements DisposableBean {
 
   private final Map scheduledTasks = new ConcurrentHashMap<>(16);
 
   @Autowired
   private TaskScheduler taskScheduler;
 
   public TaskScheduler getScheduler() {
      return this.taskScheduler;
   }
 
   public void addCronTask(Runnable task, String cronExpression) {
      addCronTask(new CronTask(task, cronExpression));
   }
 
   public void addCronTask(CronTask cronTask) {
      if (cronTask != null) {
         Runnable task = cronTask.getRunnable();
         if (this.scheduledTasks.containsKey(task)) {
            removeCronTask(task);
         }
 
         this.scheduledTasks.put(task, scheduleCronTask(cronTask));
      }
   }
 
   public void removeCronTask(Runnable task) {
      ScheduledTask scheduledTask = this.scheduledTasks.remove(task);
      if (scheduledTask != null)
         scheduledTask.cancel();
   }
 
   public ScheduledTask scheduleCronTask(CronTask cronTask) {
      ScheduledTask scheduledTask = new ScheduledTask();
      scheduledTask.future = this.taskScheduler.schedule(cronTask.getRunnable(), cronTask.getTrigger());
 
      return scheduledTask;
   }
 
 
   @Override
   public void destroy() {
      for (ScheduledTask task : this.scheduledTasks.values()) {
         task.cancel();
      }
 
      this.scheduledTasks.clear();
   }
}

定时任务示例类

@Slf4j
@Component("taskDemo")
public class Task1 {  
    public void taskByParams(String params) {
        log.info("taskByParams执行时间:{}", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        log.info("taskByParams执行有参示例任务:{}",params);
    }  
  
    public void taskNoParams() {
        log.info("taskByParams执行时间:{}", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        log.info("taskNoParams执行无参示例任务");
    }
 
    public void test(String params) {
        log.info("test执行时间:{}", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        log.info("test执行有参示例任务:{}",params);
    }
}

数据库表设计

CREATE TABLE `schedule_setting` (
  `job_id` int NOT NULL AUTO_INCREMENT COMMENT '任务ID',
  `bean_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'bean名称',
  `method_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '方法名称',
  `method_params` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '方法参数',
  `cron_expression` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'cron表达式',
  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注',
  `job_status` int DEFAULT NULL COMMENT '状态(1正常 0暂停)',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime DEFAULT NULL COMMENT '修改时间',
  PRIMARY KEY (`job_id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

实体类

@Data
public class ScheduleSetting extends Model {
   /**
    * 任务ID
    */
   @Id
   private Integer jobId;
   /**
    * bean名称
    */
   private String beanName;
   /**
    * 方法名称
    */
   private String methodName;
   /**
    * 方法参数
    */
   private String methodParams;
   /**
    * cron表达式
    */
   private String cronExpression;
   /**
    * 状态(1正常 0暂停)
    */
   private Integer jobStatus;
   /**
    * 备注
    */
   private String remark;
   /**
    * 创建时间
    */
   private Date createTime;
   /**
    * 更新时间
    */
   private Date updateTime;
}

定时任务预热

spring boot项目启动完成后,加载数据库里状态为正常的定时任务

@Service  
public class SysJobRunner implements CommandLineRunner {  
  
    private static final Logger logger = LoggerFactory.getLogger(SysJobRunner.class);  
  
    @Autowired  
    private CronTaskRegistrar cronTaskRegistrar;  
  
    @Override  
    public void run(String... args) {  
        // 初始加载数据库里状态为正常的定时任务  
        ScheduleSetting existedSysJob = new ScheduleSetting();
        List jobList = existedSysJob.selectList(new QueryWrapper().eq("job_status", 1));
        if (CollectionUtils.isNotEmpty(jobList)) {  
            for (ScheduleSetting job : jobList) {  
                SchedulingRunnable task = new SchedulingRunnable(job.getBeanName(), job.getMethodName(), job.getMethodParams());  
                cronTaskRegistrar.addCronTask(task, job.getCronExpression());  
            }  
            logger.info("定时任务已加载完毕...");  
        }  
    }  
}

工具类

用来从spring容器里获取bean

@Component  
public class SpringContextUtils implements ApplicationContextAware {  
  
    private static ApplicationContext applicationContext;  
  
    @Override  
    public void setApplicationContext(ApplicationContext applicationContext)  
            throws BeansException {  
        SpringContextUtils.applicationContext = applicationContext;  
    }  
  
    public static Object getBean(String name) {  
        return applicationContext.getBean(name);  
    }  
  
    public static  T getBean(Class requiredType) {  
        return applicationContext.getBean(requiredType);  
    }  
  
    public static  T getBean(String name, Class requiredType) {  
        return applicationContext.getBean(name, requiredType);  
    }  
  
    public static boolean containsBean(String name) {  
        return applicationContext.containsBean(name);  
    }  
  
    public static boolean isSingleton(String name) {  
        return applicationContext.isSingleton(name);  
    }  
  
    public static Class getType(String name) {  
        return applicationContext.getType(name);  
    }  
}

定时任务的:增/删/改/启动/暂停

@RestController
public class TestController {
 
   @Autowired
   private CronTaskRegistrar cronTaskRegistrar;
 
   /**
    * 添加定时任务
    *
    * @param sysJob
    * @return
    */
   @PostMapping("add")
   public boolean add(@RequestBody ScheduleSetting sysJob) {
      sysJob.setCreateTime(new Date());
      sysJob.setUpdateTime(new Date());
 
      boolean insert = sysJob.insert();
      if (!insert) {
         return false;
      }else {
         if (sysJob.getJobStatus().equals(1)) {// 添加成功,并且状态是1,直接放入任务器
            SchedulingRunnable task = new SchedulingRunnable(sysJob.getBeanName(), sysJob.getMethodName(), sysJob.getMethodParams());
            cronTaskRegistrar.addCronTask(task, sysJob.getCronExpression());
         }
      }
      return insert;
   }
 
   /**
    * 修改定时任务
    *
    * @param sysJob
    * @return
    */
   @PostMapping("update")
   public boolean update(@RequestBody ScheduleSetting sysJob) {
      sysJob.setCreateTime(new Date());
      sysJob.setUpdateTime(new Date());
 
      // 查询修改前任务
      ScheduleSetting existedSysJob = new ScheduleSetting();
      existedSysJob = existedSysJob.selectOne(new QueryWrapper().eq("job_id", sysJob.getJobId()));
      // 修改任务
      boolean update = sysJob.update(new UpdateWrapper().eq("job_id", sysJob.getJobId()));
      if (!update) {
         return false;
      } else {
         // 修改成功,则先删除任务器中的任务,并重新添加
         SchedulingRunnable task1 = new SchedulingRunnable(existedSysJob.getBeanName(), existedSysJob.getMethodName(), existedSysJob.getMethodParams());
         cronTaskRegistrar.removeCronTask(task1);
         if (sysJob.getJobStatus().equals(1)) {// 如果修改后的任务状态是1就加入任务器
            SchedulingRunnable task = new SchedulingRunnable(sysJob.getBeanName(), sysJob.getMethodName(), sysJob.getMethodParams());
            cronTaskRegistrar.addCronTask(task, sysJob.getCronExpression());
         }
      }
      return update;
   }
 
   /**
    * 删除任务
    *
    * @param jobId
    * @return
    */
   @PostMapping("del/{jobId}")
   public boolean del(@PathVariable("jobId") Integer jobId) {
      // 先查询要删除的任务信息
      ScheduleSetting existedSysJob = new ScheduleSetting();
      existedSysJob = existedSysJob.selectOne(new QueryWrapper().eq("job_id", jobId));
 
      // 删除
      boolean del = existedSysJob.delete(new QueryWrapper().eq("job_id", jobId));
      if (!del)
         return false;
      else {// 删除成功时要清除定时任务器中的对应任务
         SchedulingRunnable task = new SchedulingRunnable(existedSysJob.getBeanName(), existedSysJob.getMethodName(), existedSysJob.getMethodParams());
         cronTaskRegistrar.removeCronTask(task);
      }
      return del;
   }
 
   // 停止/启动任务
   @PostMapping("changesStatus/{jobId}/{stop}")
   public boolean changesStatus(@PathVariable("jobId") Integer jobId, @PathVariable("stop") Integer stop) {
      // 修改任务状态
      ScheduleSetting scheduleSetting = new ScheduleSetting();
      scheduleSetting.setJobStatus(stop);
      boolean job_id = scheduleSetting.update(new UpdateWrapper().eq("job_id", jobId));
      if (!job_id) {
         return false;
      }
      // 查询修改后的任务信息
      ScheduleSetting existedSysJob = new ScheduleSetting();
      existedSysJob = existedSysJob.selectOne(new QueryWrapper().eq("job_id", jobId));
 
      // 如果状态是1则添加任务
      if (existedSysJob.getJobStatus().equals(1)) {
         SchedulingRunnable task = new SchedulingRunnable(existedSysJob.getBeanName(), existedSysJob.getMethodName(), existedSysJob.getMethodParams());
         cronTaskRegistrar.addCronTask(task, existedSysJob.getCronExpression());
      } else {
         // 否则清除任务
         SchedulingRunnable task = new SchedulingRunnable(existedSysJob.getBeanName(), existedSysJob.getMethodName(), existedSysJob.getMethodParams());
         cronTaskRegistrar.removeCronTask(task);
      }
      return true;
   }

SpringBoot动态定时任务如何实现

cron

cron表达式语法:

[秒] [分] [小时] [日] [月] [周] [年]

 注:[年]不是必须的域,可以省略[年],则一共6个域

SpringBoot动态定时任务如何实现

通配符说明:

  • * 表示所有值。 例如:在分的字段上设置 *,表示每一分钟都会触发。

  • ? 表示不指定值。使用的场景为不需要关心当前设置这个字段的值。例如:要在每月的10号触发一个操作,但不关心是周几,所以需要周位置的那个字段设置为”?” 具体设置为 0 0 0 10 * ?

  • - 表示区间。例如 在小时上设置 “10-12”,表示 10,11,12点都会触发。

  • , 表示指定多个值,例如在周字段上设置 “MON,WED,FRI” 表示周一,周三和周五触发

  • / 用于递增触发。设定秒数为“5/15”,表示从第5秒开始,每隔15秒触发一次(即第5秒、第20秒、第35秒和第50秒)。 在日字段上设置’1/3’所示每月1号开始,每隔三天触发一次。

  • L 表示最后的意思。在日字段设置上,表示当月的最后一天(依据当前月份,如果是二月还会依据是否是润年[leap]), 在周字段上表示星期六,相当于”7”或”SAT”。如果在”L”前加上数字,则表示该数据的最后一个。例如在周字段上设置”6L”这样的格式,则表示“本月最后一个星期五”

    云模块网站管理系统3.1.03
    云模块网站管理系统3.1.03

    云模块_YunMOK网站管理系统采用PHP+MYSQL为编程语言,搭载自主研发的模块化引擎驱动技术,实现可视化拖拽无技术创建并管理网站!如你所想,无限可能,支持创建任何网站:企业、商城、O2O、门户、论坛、人才等一块儿搞定!永久免费授权,包括商业用途; 默认内置三套免费模板。PC网站+手机网站+适配微信+文章管理+产品管理+SEO优化+组件扩展+NEW Login界面.....目测已经遥遥领先..

    下载
  • W 表示离指定日期的最近那个工作日(周一至周五). 例如在日字段上置”15W”,表示离每月15号最近的那个工作日触发。如果15号正好是周六,则找最近的周五(14号)触发, 如果15号是周未,则找最近的下周一(16号)触发.如果15号正好在工作日(周一至周五),则就在该天触发。当指定格式为“1W”时,它表示在每月的1号后的最近工作日触发。如果1号正是周六,则将在3号下周一触发。(注,”W”前只能设置具体的数字,不允许区间”-“)。

  • # 序号(表示每月的第几个周几),例如在周字段上设置”6#3”表示在每月的第三个周六.注意如果指定”#5”,正好第五周没有周六,则不会触发该配置(用在母亲节和父亲节再合适不过了) ;小提示:’L’和 ‘W’可以一组合使用。如果在日字段上设置”LW”,则表示在本月的最后一个工作日触发;周字段的设置,若使用英文字母是不区分大小写的,即MON与mon相同。

示例:

每隔5秒执行一次:*/5 * * * * ?

每隔1分钟执行一次:0 */1 * * * ?

每天23点执行一次:0 0 23 * * ?

每天凌晨1点执行一次:0 0 1 * * ?

每月1号凌晨1点执行一次:0 0 1 1 * ?

每月最后一天23点执行一次:0 0 23 L * ?

每周星期六凌晨1点实行一次:0 0 1 ? * L

在26分、29分、33分执行一次:0 26,29,33 * * * ?

每天的0点、13点、18点、21点都执行一次:0 0 0,13,18,21 * * ?

cron在线表达式生成器:http://tools.jb51.net/code/Quartz_Cron_create

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
Python 自然语言处理(NLP)基础与实战
Python 自然语言处理(NLP)基础与实战

本专题系统讲解 Python 在自然语言处理(NLP)领域的基础方法与实战应用,涵盖文本预处理(分词、去停用词)、词性标注、命名实体识别、关键词提取、情感分析,以及常用 NLP 库(NLTK、spaCy)的核心用法。通过真实文本案例,帮助学习者掌握 使用 Python 进行文本分析与语言数据处理的完整流程,适用于内容分析、舆情监测与智能文本应用场景。

9

2026.01.27

拼多多赚钱的5种方法 拼多多赚钱的5种方法
拼多多赚钱的5种方法 拼多多赚钱的5种方法

在拼多多上赚钱主要可以通过无货源模式一件代发、精细化运营特色店铺、参与官方高流量活动、利用拼团机制社交裂变,以及成为多多进宝推广员这5种方法实现。核心策略在于通过低成本、高效率的供应链管理与营销,利用平台社交电商红利实现盈利。

107

2026.01.26

edge浏览器怎样设置主页 edge浏览器自定义设置教程
edge浏览器怎样设置主页 edge浏览器自定义设置教程

在Edge浏览器中设置主页,请依次点击右上角“...”图标 > 设置 > 开始、主页和新建标签页。在“Microsoft Edge 启动时”选择“打开以下页面”,点击“添加新页面”并输入网址。若要使用主页按钮,需在“外观”设置中开启“显示主页按钮”并设定网址。

13

2026.01.26

苹果官方查询网站 苹果手机正品激活查询入口
苹果官方查询网站 苹果手机正品激活查询入口

苹果官方查询网站主要通过 checkcoverage.apple.com/cn/zh/ 进行,可用于查询序列号(SN)对应的保修状态、激活日期及技术支持服务。此外,查找丢失设备请使用 iCloud.com/find,购买信息与物流可访问 Apple (中国大陆) 订单状态页面。

116

2026.01.26

npd人格什么意思 npd人格有什么特征
npd人格什么意思 npd人格有什么特征

NPD(Narcissistic Personality Disorder)即自恋型人格障碍,是一种心理健康问题,特点是极度夸大自我重要性、需要过度赞美与关注,同时极度缺乏共情能力,背后常掩藏着低自尊和不安全感,影响人际关系、工作和生活,通常在青少年时期开始显现,需由专业人士诊断。

6

2026.01.26

windows安全中心怎么关闭 windows安全中心怎么执行操作
windows安全中心怎么关闭 windows安全中心怎么执行操作

关闭Windows安全中心(Windows Defender)可通过系统设置暂时关闭,或使用组策略/注册表永久关闭。最简单的方法是:进入设置 > 隐私和安全性 > Windows安全中心 > 病毒和威胁防护 > 管理设置,将实时保护等选项关闭。

6

2026.01.26

2026年春运抢票攻略大全 春运抢票攻略教你三招手【技巧】
2026年春运抢票攻略大全 春运抢票攻略教你三招手【技巧】

铁路12306提供起售时间查询、起售提醒、购票预填、候补购票及误购限时免费退票五项服务,并强调官方渠道唯一性与信息安全。

112

2026.01.26

个人所得税税率表2026 个人所得税率最新税率表
个人所得税税率表2026 个人所得税率最新税率表

以工资薪金所得为例,应纳税额 = 应纳税所得额 × 税率 - 速算扣除数。应纳税所得额 = 月度收入 - 5000 元 - 专项扣除 - 专项附加扣除 - 依法确定的其他扣除。假设某员工月工资 10000 元,专项扣除 1000 元,专项附加扣除 2000 元,当月应纳税所得额为 10000 - 5000 - 1000 - 2000 = 2000 元,对应税率为 3%,速算扣除数为 0,则当月应纳税额为 2000×3% = 60 元。

31

2026.01.26

oppo云服务官网登录入口 oppo云服务登录手机版
oppo云服务官网登录入口 oppo云服务登录手机版

oppo云服务https://cloud.oppo.com/可以在云端安全存储您的照片、视频、联系人、便签等重要数据。当您的手机数据意外丢失或者需要更换手机时,可以随时将这些存储在云端的数据快速恢复到手机中。

95

2026.01.26

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Redis6入门到精通超详细教程
Redis6入门到精通超详细教程

共47课时 | 5.3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号