独家完整版!SpringBoot动态定时任务来了!

img

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

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 {  @Beanpublic 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;}@Overridepublic 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);}@Overridepublic 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);}@Overridepublic 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<Runnable, ScheduledTask> scheduledTasks = new ConcurrentHashMap<>(16);@Autowiredprivate 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;}@Overridepublic 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<ScheduleSetting> {/*** 任务ID*/@Idprivate 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<ScheduleSetting> jobList = existedSysJob.selectList(new QueryWrapper<ScheduleSetting>().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> T getBean(Class<T> requiredType) {  return applicationContext.getBean(requiredType);  }  public static <T> T getBean(String name, Class<T> 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<? extends Object> getType(String name) {  return applicationContext.getType(name);  }  }

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

@RestController
public class TestController {@Autowiredprivate 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<ScheduleSetting>().eq("job_id", sysJob.getJobId()));// 修改任务boolean update = sysJob.update(new UpdateWrapper<ScheduleSetting>().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<ScheduleSetting>().eq("job_id", jobId));// 删除boolean del = existedSysJob.delete(new QueryWrapper<ScheduleSetting>().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<ScheduleSetting>().eq("job_id", jobId));if (!job_id) {return false;}// 查询修改后的任务信息ScheduleSetting existedSysJob = new ScheduleSetting();existedSysJob = existedSysJob.selectOne(new QueryWrapper<ScheduleSetting>().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;}

最后说一句(求关注!别白嫖!)

如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、转发、在看。

关注公众号:woniuxgg,在公众号中回复:笔记  就可以获得蜗牛为你精心准备的java实战语雀笔记,回复面试、开发手册、有超赞的粉丝福利!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2775997.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

236. 二叉树的最近公共祖先 - 力扣(LeetCode)

题目描述 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个节点 p、q&#xff0c;最近公共祖先表示为一个节点 x&#xff0c;满足 x 是 p、q 的祖先且 x 的深度尽可能大&#xff08;一个节点也可以…

雨云2h2g香港二区云服务器测评(纯测评)

购买并且重装好系统后&#xff0c;来itdog去ping一下看看延迟怎么样。&#xff08;香港无移动屏蔽&#xff09;&#xff1a; 然后&#xff0c;我们来做一个线路路由测试&#xff08;去回程路由测试&#xff09;。&#xff08;雨云香港服务器IP不是原生IP&#xff0c;而是广播IP…

【Spring】Spring 对 Ioc 的实现

一、Ioc 控制反转 控制反转是一种思想 控制反转是为了降低程序耦合度&#xff0c;提高程序扩展力&#xff0c;达到 OCP 原则&#xff0c;达到 DIP 原则 控制反转&#xff0c;反转的是什么&#xff1f; 将对象的创建权利交出去&#xff0c;交给第三方容器负责 将对象和对象之…

会声会影绿幕抠图操作方法 会声会影绿幕抠图有绿色残边 绿幕抠图视频有绿边怎么处理 抖音怎么剪辑视频 视频剪辑软件推荐

科幻片里真的存在怪兽吗&#xff1f;外太空的画面是直接将演员放入太空拍摄的吗?其实这些不切实际的画面是通过绿幕拍摄实现的。你只需要在绿幕前拍一段太空漫步的视频&#xff0c;再利用会声会影的抠图功能就能实现&#xff01;如果你还不会绿幕抠图&#xff0c;我今天就手把…

Vue.js2+Cesium1.103.0 十五、绘制视锥,并可实时调整视锥姿态

Vue.js2Cesium1.103.0 十五、绘制视锥&#xff0c;并可实时调整视锥姿态 Demo <template><divid"cesium-container"style"width: 100%; height: 100%;"/> </template><script> /* eslint-disable no-undef */ /* eslint-disable …

微信小程序的图片色彩分析,窃取网络图片的主色调

1、安装 Mini App Color Thief 包 包括下载包&#xff0c;简单使用都有&#xff0c;之前写了&#xff0c;这里就不写了 网址&#xff1a;微信小程序的图片色彩分析&#xff0c;窃取主色调&#xff0c;调色板-CSDN博客 2、 问题和解决方案 问题&#xff1a;由于我们的窃取图片的…

娅奴服饰:行至云深处,问计新零售

编辑&#xff1a;阿冒 设计&#xff1a;沐由 大浪壮美&#xff0c;时尚前行。 作为广东省首批特色小镇创建示范点&#xff0c;以及粤港澳大湾区唯一的特色时尚小镇&#xff0c;大浪时尚小镇云集了700余家服装及配套企业&#xff0c;涌动着蓬勃的生机与无尽的活力。 国内知名的“…

API网关架构设计与实现的经验总结与实践

API网关是现代微服务架构中的重要组件&#xff0c;它充当了前端和后端微服务之间的中介。本文将介绍API网关的架构设计原则和实现方法&#xff0c;以帮助开发人员更好地理解和应用这些技术。 1. 什么是API网关&#xff1f; - 解释了API网关的基本概念和作用&#xff0c;以及…

API接口访问鉴权设计和实现的经验总结

API接口访问鉴权是保护API资源安全的重要措施。本文总结了一些常见的API接口访问鉴权设计和实现方法&#xff0c;以帮助开发人员更好地理解和应用这些技术。 1. 什么是API接口访问鉴权&#xff1f; - 解释了API接口访问鉴权的基本概念和作用&#xff0c;以及为什么需要对A…

如何轻松恢复已删除/未保存的 Word 文档

经过几个小时的编写和编辑后&#xff0c;您的计算机决定崩溃&#xff0c;或者您不小心删除了您一直在努力处理的同一个文件。听起来像一场噩梦&#xff0c;对吧&#xff1f;不幸的是&#xff0c;任何人都可能遇到这种情况&#xff0c;这就是为什么我们整理了这份经过尝试和测试…

Web后端开发:事务与AOP

事务管理 在学习数据库时&#xff0c;讲到&#xff1a;事务是一组操作的集合&#xff0c;它是一个不可分割的工作单位。事务会把所有的操作作为一个整体&#xff0c;一起向数据库提交或者是撤销操作请求&#xff0c;要么同时成功&#xff0c;要么同时失败。 事务的操作主要有三…

【开源】SpringBoot框架开发校园电商物流云平台

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 商品数据模块2.3 快递公司模块2.4 物流订单模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 商品表3.2.2 快递公司表3.2.3 物流订单表 四、系统展示五、核心代码5.1 查询商品5.2 查询快递公司5.3 查…

在虚拟机上搭建CentOS环境并配置静态IP

在虚拟机上搭建CentOS环境并配置静态IP 在进行Linux系统的学习和实践时&#xff0c;搭建一个本地的CentOS环境是一个非常好的方式。本文将介绍如何使用虚拟机&#xff08;VM&#xff09;搭建CentOS环境&#xff0c;并配置静态IP&#xff0c;以便更好地进行网络管理和测试。 步…

STM32输出PWM波控制180°舵机

时间记录&#xff1a;2024/2/8 一、PWM介绍 &#xff08;1&#xff09;脉冲宽度调制 &#xff08;2&#xff09;占空比&#xff1a;高电平时间占整个周期时间的比例 &#xff08;3&#xff09;STM32通过定时器实现PWM时具有两种模式 PWM1模式&#xff1a;向上计数模式下&…

Java面向对象 方法的重写

目录 重写重写的规则实例创建Person类创建Student类测试 重载和重写的区别 重写 发生在子类和父类中&#xff0c;当子类对父类提供的方法不满意的时候&#xff0c;要对父类的方法进行重写。 重写的规则 子类的方法名字和父类必须一致&#xff0c;参数列表&#xff08;个数&…

v-if 和v-for的联合规则及示例

第073个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下&#xff0c;本专栏提供行之有效的源代码示例和信息点介绍&#xff0c;做到灵活运用。 提供vue2的一些基本操作&#xff1a;安装、引用&#xff0c;模板使用&#xff0c;computed&a…

《低功耗方法学》翻译——附录B:UPF命令语法

附录B&#xff1a;UPF命令语法 本章介绍了文本中引用的所选UPF命令的语法。 节选自“统一电源格式&#xff08;UPF&#xff09;标准&#xff0c;1.0版”&#xff0c;经该Accellera许可复制。版权所有&#xff1a;(c)2006-2007。Accellera不声明或代表摘录材料的准确性或内容&…

为什么有人不推荐选择网络安全行业?

很多人是这样了解网络安全专业的&#xff1a; 专业就业面窄&#xff0c;市场需求量小。网络空间安全主要是从事网络安全方面的工作&#xff0c;就业面窄。它和软件工程、计算机科学与技术等专业不一样&#xff0c;后者毕业可以从事软件开发或者大数据等方面的工作。而网络安全就…

OpenMMLab

https://openmmlab.com/codebaseOpenMMLab是深度学习时代最完整的计算机视觉开源算法体系。自2018 年开源以来&#xff0c;累计发布超过 20个算法库&#xff0c;涵盖分类、检测、分割、视频理解等众多算法领域&#xff0c;有超过 250 种算法实现和 2000 个预训练模型。OpenMMLa…

Optimism为 CQT提供价值 20 万美元的生态系统资助,以表彰其支持

Covalent Network&#xff08;CQT&#xff09; 是 Web3 生态系统中关键的“数据可用性”层&#xff0c;在与 Optimism Collective 多年的合作中取得了骄人的成果。Covalent Network&#xff08;CQT&#xff09;对于 Optimism 跨链数据的增长产生了直接的影响&#xff0c;而这一…