如何把Java的定时任务写到数据库里面去配置?

之前是这样写的,每次要改定时器都要修改发版,很麻烦:

package cn.net.cdsz.ccb.common.scheduled;import cn.net.cdsz.ccb.business.config.Custom;
import cn.net.cdsz.ccb.business.service.CCBBankService;
import cn.net.cdsz.ccb.business.service.CCBTestSetService;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;/*** 这个是每6小时(程序启动)执行一次的   */
@Component
@EnableAsync
public class correctMoney {@Autowiredprivate Custom custom  ;private Logger logger = LogManager.getLogger();@Autowiredprivate CCBTestSetService cCBTestSetService;//@Scheduled(fixedRate = 60000)   // 60000每隔一分钟执行一次@Scheduled(cron = " 0 0 0 * * ?") // 每天凌晨执行一次 ,专业  [秒] [分] [小时] [日] [月] [周] [年]//@Scheduled(cron = " 0 * * * * ?")//这样是每3秒执行一次了,专业  [秒] [分] [小时] [日] [月] [周] [年]public void run() {if(custom.getIsscheduled()){try {run(()-> {cCBTestSetService.AutomaticDeductionByDay(); });}catch (RuntimeException e){logger.error(e.getMessage());}catch (Exception e){logger.error(e);}}}public void run(Runnable runnable) {runnable.run();}}

现在改成数据库里面去配置定时器了,就容易了很多,上代码:

package cn.net.cdsz.ccb.common.scheduled;import club.newepoch.utils.JsonUtils;
import club.newepoch.utils.StringUtils;
import cn.net.cdsz.ccb.business.model.pojo.ScheduledTask;
import cn.net.cdsz.ccb.business.model.pojo.ScheduledTaskLog;
import cn.net.cdsz.ccb.common.bean.BaseHolder;
import cn.net.cdsz.ccb.common.event.GenTables;
import lombok.SneakyThrows;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Service;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;/*** 动态调度器(配置写数据库)*/
@Service
public class DynamicScheduler {final private TaskScheduler taskScheduler;final private  GenTables genTables;private Map<Long, ScheduledFuture<?>> jobsMap = new ConcurrentHashMap<>();private Map<Long, String> taskCronMap = new ConcurrentHashMap<>();//自己维护一个调度时间的表public DynamicScheduler(TaskScheduler taskScheduler, GenTables genTables) {this.genTables = genTables;this.taskScheduler = taskScheduler;}// 这个方法用来启动所有的active任务//@PostConstructpublic void startActiveJobs() {ScheduledTask scheduledTaskSql = new ScheduledTask();List<ScheduledTask> tasks = genTables.queryMore(scheduledTaskSql);tasks.forEach(this::scheduleTask);}// 用于调度任务public void scheduleTask(ScheduledTask task) {if ("1".equals(task.getIsActive())) {ScheduledFuture<?> scheduledTask = taskScheduler.schedule(() -> runTask(task),new CronTrigger(task.getCronExpression(), TimeZone.getTimeZone(TimeZone.getDefault().getID())));jobsMap.put(task.getKeyId(), scheduledTask);taskCronMap.put(task.getKeyId(), task.getCronExpression());} else {cancelTask(task.getKeyId());}}// 用于取消计划中的任务public void cancelTask(Long taskId) {ScheduledFuture<?> scheduledTask = jobsMap.get(taskId);if (scheduledTask != null) {scheduledTask.cancel(true);jobsMap.remove(taskId);taskCronMap.remove(taskId);}}@SneakyThrows// 调用转换函数,将字符串值转换为对应的对象类型private Object convertStringToObject(String value, Class<?> type) {if (String.class == type) {return value;} else if (Integer.class == type || int.class == type) {return Integer.valueOf(value);} else if (Double.class == type || double.class == type) {return Double.valueOf(value);}// 可以根据需要添加更多类型的转换throw new IllegalArgumentException("Unsupported type: " + type);}private void runTask(ScheduledTask task) {// 这里执行你的任务逻辑9 15Object bean = BaseHolder.getBean(task.getBeanStr());    // 获取bean实例String methodName = task.getExecMath();          // 从数据库获取的方法名String paramTypeNamesStr = task.getParamTypeNamesStr(); // 从数据库获取的参数类型名字符串String paramValuesStr = task.getParamValuesStr();    // 从数据库获取的参数值字符串// 使用.split(", ")方法来分割字符串并转换为数组,然后将数组转换为列表List<String> paramTypeNames = Arrays.asList(paramTypeNamesStr.split(","));  // 方法参数类型List<String> paramValues = Arrays.asList(paramValuesStr.split(","));// 方法参数值// 将字符串类型名称转换为Class类型对象Class<?>[] parameterTypes = new Class<?>[paramTypeNames.size()];for (int i = 0; i < paramTypeNames.size(); i++) {try {if(StringUtils.isBlank(paramTypeNames.get(i))){continue;}parameterTypes[i] = Class.forName(paramTypeNames.get(i));} catch (ClassNotFoundException e) {throw new RuntimeException(e);}}// 将字符串参数值转换为相应的对象Object[] parameters = new Object[paramValues.size()];for (int i = 0; i < paramValues.size(); i++) {if(StringUtils.isBlank(paramValues.get(i))){continue;}String value = paramValues.get(i);Class<?> type = parameterTypes[i];// 调用转换函数,将字符串值转换为对应的对象类型parameters[i] = convertStringToObject(value, type);}// 写日志。。。ScheduledTaskLog  scheduledTaskLog = new ScheduledTaskLog();scheduledTaskLog.setTaskName(task.getTaskName());scheduledTaskLog.setCronExpression(task.getCronExpression());scheduledTaskLog.setBeanStr(task.getBeanStr());scheduledTaskLog.setExecMath(task.getExecMath());scheduledTaskLog.setParamTypeNamesStr(task.getParamTypeNamesStr());scheduledTaskLog.setParamValuesStr(task.getParamValuesStr());// ... 接下来是通过反射调用方法的过程 ...try {Method method;// 判断是否有参数类型存在if ((parameterTypes == null || parameterTypes.length == 0) || (parameters == null || Arrays.stream(parameters).allMatch(Objects::isNull))) {// 如果没有参数类型则认为是不带参数的方法method = bean.getClass().getMethod(methodName);// 使用.invoke()调用方法,传入bean和参数数组Object result = method.invoke(bean);scheduledTaskLog.setResultStr(JsonUtils.toJSONString(result));} else {// 获取具有指定参数类型的方法对象method = bean.getClass().getMethod(methodName, parameterTypes);// 使用.invoke()调用方法,传入bean和参数数组Object result = method.invoke(bean, parameters);scheduledTaskLog.setResultStr(JsonUtils.toJSONString(result));}//保存调度的执行日志genTables.save(scheduledTaskLog);// 处理调用结果} catch (NoSuchMethodException e) {e.printStackTrace();// 处理没有找到具有指定参数类型的方法的情况} catch (Exception e) {e.printStackTrace();// 处理其他可能的异常}}// 你可以通过定时任务,周期性地从数据库中获取最新的任务配置@Scheduled(fixedRate = 1000*60) //单位是毫秒(ms)public void refreshActiveJobs() {// 查询数据库中所有active的任务并更新调度ScheduledTask scheduledTaskSql = new ScheduledTask();List<ScheduledTask> tasks = genTables.queryMore(scheduledTaskSql);for (int i = 0; i < tasks.size(); i++) {ScheduledTask task = tasks.get(i);if (!jobsMap.containsKey(task.getKeyId())) {// 如果在内存中不存在,则为新任务,需要调度scheduleTask(task);} else {// 如果已经存在,检查cron表达式是否更新String cronStr = taskCronMap.get(task.getKeyId());// 如果内容不一样,那么就修改这个计划if(!cronStr.equals(task.getCronExpression())){// Cron表达式已更改,重新调度cancelTask(task.getKeyId());scheduleTask(task);}}}// 取消已经被设置为非active的任务for (Map.Entry<Long, ScheduledFuture<?>> entry : jobsMap.entrySet()) {Long taskId = entry.getKey();ScheduledTask scheduledTaskSql2 = new ScheduledTask();scheduledTaskSql2.setKeyId(taskId);ScheduledTask scheduledTask = genTables.queryOne(scheduledTaskSql2);if (scheduledTask !=null && "0".equals(scheduledTask.getIsActive())) {cancelTask(taskId);}}}
}

上一个线程的辅助类:

    package cn.net.cdsz.ccb.common.config.app;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 AppConfig {@Beanpublic TaskScheduler taskScheduler() {ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();// 设定线程池大小,可以根据实际情况调整scheduler.setPoolSize(5);// 设置线程名称前缀scheduler.setThreadNamePrefix("TaskScheduler-");// 线程池关闭前的最大等待时间,确保所有任务都能完成scheduler.setAwaitTerminationSeconds(600);// 设置当调度器shutdown被调用时等待当前被调度的任务完成scheduler.setWaitForTasksToCompleteOnShutdown(true);// 初始化线程池scheduler.initialize();return scheduler;}
}

上数据库表的sql:

CREATE TABLE `scheduled_task` (`key_id` bigint(19) NOT NULL AUTO_INCREMENT,`task_name` varchar(255) NOT NULL DEFAULT '' COMMENT '任务名字',`cron_expression` varchar(255) NOT NULL DEFAULT '' COMMENT '周期配置比如:0 30 0 * * ?',`is_active` tinyint(4) NOT NULL DEFAULT '1' COMMENT '是否有效',`bean_str` varchar(255) NOT NULL DEFAULT '' COMMENT '获取bean实例',`exec_math` varchar(255) NOT NULL DEFAULT '' COMMENT '从数据库获取的方法名,写一个',`param_type_names_str` varchar(255) NOT NULL DEFAULT '' COMMENT '从数据库获取的参数类型名字符串,用英文,隔开',`param_values_str` varchar(255) NOT NULL DEFAULT '' COMMENT '从数据库获取的参数值字符串,用英文,隔开',`add_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,`modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,`lived` tinyint(4) NOT NULL DEFAULT '0',PRIMARY KEY (`key_id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='动态调度(手动配置的定时器)';CREATE TABLE `scheduled_task_log` (`key_id` bigint(19) NOT NULL AUTO_INCREMENT,`task_name` varchar(255) NOT NULL DEFAULT '',`cron_expression` varchar(255) NOT NULL DEFAULT '',`bean_str` varchar(255) NOT NULL DEFAULT '',`exec_math` varchar(255) NOT NULL DEFAULT '',`param_type_names_str` varchar(255) NOT NULL DEFAULT '',`param_values_str` varchar(255) NOT NULL DEFAULT '',`result_str` text,`add_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,`modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,`lived` tinyint(4) NOT NULL DEFAULT '0',PRIMARY KEY (`key_id`)
) ENGINE=InnoDB AUTO_INCREMENT=36625 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;

上截图:

注意:把这个加上,固定好类在spring容器中的类名,要和数据库中的数据保存一致!

完毕!

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue脚手架和vite创建的项目的环境配置

开发环境文件 .env.development NODE_ENV"development" # // 开发接口域名 本地测试就用这个 # vue脚手架创建的 VUE_APP_MODE"开发环境" VUE_APP_API_URL http://19527 # vite创建的 # VITE_MODE"开发环境" # VITE_BASE_URL http://1920:9527…

【极速前进】20240423-20240428:Phi-3、fDPO、TextSquare多模态合成数据、遵循准则而不是偏好标签、混合LoRA专家

一、Phi-3技术报告 论文地址&#xff1a;https://arxiv.org/pdf/2404.14219 ​ 发布了phi-3-mini&#xff0c;一个在3.3T token上训练的3.8B模型。在学术基准和内部测试中的效果都优于Mixtral 8*7B和GPT-3.5。此外&#xff0c;还发布了7B和14B模型phi-3-small和phi-3-medium。…

Puppeteer的基本使用及多目标同时访问

文章目录 一、安装 puppeteer 并更改默认缓存路径1、更改 Puppeteer 用于安装浏览器的默认缓存目录2、安装 puppeteer3、项目结构目录 二、基本使用1、启动浏览器并访问目标网站2、生成截图3、生成 PDF 文件4、获取目标网站 html 结构并解析5、拦截请求6、执行 JavaScript7、同…

网络工程师必备知识点

网络工程的基础知识 随着信息化建设不断深入&#xff0c;网络工程项目的实施也越发重要。网络工程涵盖多个方面&#xff0c;例如局域网、广域网、互联网、安全系统等。网络工程项目的实施是建立在信息化技术的基础上的企业信息化建设过程。在网络工程项目实施的过程中&#xf…

OFD(Open Fixed-layout Document)

OFD(Open Fixed-layout Document) &#xff0c;是由工业和信息化部软件司牵头中国电子技术标准化研究院成立的版式编写组制定的版式文档国家标准&#xff0c;属于中国的一种自主格式&#xff0c;要打破政府部门和党委机关电子公文格式不统一&#xff0c;以方便地进行电子文档的…

资源管理器CPU占用太高

最近资源管理器经常飙到80%-100%&#xff0c;所以电脑很卡。比如下面的新打开一个文件目录就这样 工具 shexview下载地址 排除 排序 先点Microsoft排序&#xff0c;上面粉色的就是所谓的外部插件 全部禁用 粉色全选->右键->Disable Selected Items &#xff08;看其他…

天锐绿盾 | 办公加密系统,源代码防泄密、源代码透明加密、防止开发部门人员泄露源码

天锐绿盾作为一款专注于数据安全与防泄密的专业解决方案&#xff0c;它确实提供了针对源代码防泄密的功能&#xff0c;帮助企业保护其核心的知识产权。 PC地址&#xff1a; https://isite.baidu.com/site/wjz012xr/2eae091d-1b97-4276-90bc-6757c5dfedee 以下是天锐绿盾可能采…

java—异常

异常 什么是异常 异常的体系 编译时异常处理方式 1、选择报错的整个代码块&#xff0c;快捷键crtlaltt键&#xff0c;选择try/catch将代码围起来。 2、编译异常处理方式2 在main方法上抛出异常 自定义异常 例子&#xff1a; 自定义运行时异常 自定义编译时异常 异常…

Misc 流量分析

流量分析简介 网络流量分析是指捕捉网络中流动的数据包&#xff0c;并通过查看包内部数据以及进行相关的协议、流量分析、统计等来发现网络运行过程中出现的问题。 在CTF比赛中&#xff0c;以及各种技能大赛对于流量包的分析取证是一种十分重要的题型。通常这类题目都是会提供…

容器Docker:轻量级虚拟化技术解析

引言 随着云计算和虚拟化技术的飞速发展&#xff0c;容器技术以其轻量级、高效、可移植的特性&#xff0c;逐渐成为了软件开发和部署的新宠。在众多容器技术中&#xff0c;Docker以其简单易用、功能强大的特点&#xff0c;赢得了广泛的关注和应用。本文将全面介绍Docker的基本概…

软件系统测试方案书(测试计划-Word原件)

2 引言 2.1 编写目的 2.3 测试人员 2.4 项目背景 2.5 测试目标 2.6 简写和缩略词 2.7 参考资料 2.8 测试提交文档 2.9 测试进度 3 测试环境 3.1 软硬件环境 4 测试工具 5 测试策略 5.1 测试阶段划分及内容 5.1.1 集成测试 5.1.2 系统测试 5.1.2.1 功能测试 5.…

第六代移动通信介绍、无线网络类型、白皮书

关于6G 即第六代移动通信的介绍&#xff0c; 图解通信原理与案例分析-30&#xff1a;6G-天地互联、陆海空一体、全空间覆盖的超宽带移动通信系统_6g原理-CSDN博客文章浏览阅读1.7w次&#xff0c;点赞34次&#xff0c;收藏165次。6G 即第六代移动通信&#xff0c;6G 将在5G 的基…