当前位置: 首页 > news >正文

SpringBoot 实现动态管理定时任务 Job的动态操作(添加、修改、启停、执行、删除)以及界面展示和具体Job的创建与执行示例

SpringBoot 实现动态管理定时任务 Job的动态操作(添加、修改、启停、执行、删除)以及界面展示和具体Job的创建与执行示例

关键接口类: CronTaskRegistrar SchedulingRunnable

. 添加定时任务注册类,用来增加、删除定时任务


import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.config.CronTask;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.ScheduledFuture;/*** @description: 添加定时任务注册类,用来增加、删除定时任务。**/
@Component
public class CronTaskRegistrar implements DisposableBean {public static final Map<Runnable, ScheduledFuture<?>> scheduledTasks = new ConcurrentHashMap<>();public static final Map<String, Runnable> runnableMap = new ConcurrentHashMap<>();@Autowiredprivate TaskScheduler taskScheduler;public TaskScheduler getScheduler() {return this.taskScheduler;}/*** 新增定时任务* @param task* @param cronExpression*/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 (scheduledTasks.containsKey(task)) {removeCronTask(task);}scheduledTasks.put(task, scheduleCronTask(cronTask));}}/*** @description: 移除定时任务*/public void removeCronTask(Runnable task) {ScheduledFuture<?> scheduledTask = scheduledTasks.get(task);if (scheduledTask != null) {scheduledTask.cancel(true);scheduledTasks.remove(task);}}public ScheduledFuture<?> scheduleCronTask(CronTask cronTask) {return this.taskScheduler.schedule(cronTask.getRunnable(), cronTask.getTrigger());}/*** @description: 定时任务销毁*/@Overridepublic void destroy() {for (ScheduledFuture<?> task : scheduledTasks.values()) {task.cancel(true);}scheduledTasks.clear();}
}

4、封装和执行定时任务


import com.yicheng.common.exception.UtilException;
import com.yicheng.common.utils.SpringContextUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils;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 runnableName;private String beanName;private String methodName;private Object[] params;public SchedulingRunnable(String beanName, String methodName) {this(beanName, methodName, null);}public SchedulingRunnable(String runnableName, String beanName, String methodName, Object... params) {this.runnableName = runnableName;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 (null != params && params.length > 0) {Class<?>[] paramCls = new Class[params.length];for (int i = 0; i < params.length; i++) {paramCls[i] = params[i].getClass();}method = target.getClass().getDeclaredMethod(methodName, paramCls);} else {method = target.getClass().getDeclaredMethod(methodName);}ReflectionUtils.makeAccessible(method);if (null != params && params.length > 0) {method.invoke(target, params);} else {method.invoke(target);}} catch (Exception ex) {logger.error(String.format("定时任务执行异常 - bean:%s,方法:%s,参数:%s ", beanName, methodName, params), ex);throw new UtilException(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);}
}

4、防止 冲突 重新配置 TaskScheduler

@Configuration
public class SchedulerConfig {@Bean()public TaskScheduler taskScheduler() {return new ConcurrentTaskScheduler();}
}

5、任务执行业务

import com.yicheng.base.service.IVehicleService;
import com.yicheng.business.instruction.service.IIssuanceSevice;
import com.yicheng.business.ttsjob.domain.TbTtsJob;
import com.yicheng.business.ttsjob.service.ITbTtsJobService;
import com.yicheng.common.scheduler.CronTaskRegistrar;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;/*** @className: DoTask* @description: tts任务处理线程* @creat: 2025/1/10 15:05*/
@Component("doTask")
public class DoTask {private static final Logger logger = LoggerFactory.getLogger(DoTask.class);public static final String jobNamePrefix = "ttsJob-";@Autowiredprivate CronTaskRegistrar cronTaskRegistrar;@Autowiredprivate ITbTtsJobService iTbTtsJobService;public DoTask() {}/*** 任务类型,1-立即执行,2-定时一次,3-每天固定时间执行** @param runnableName* @param type* @param jobid*/public void ttsSend(String runnableName, Integer type, Long jobid) {logger.info("即将执行ttsTask:type={}, jobid={}", type, jobid);TbTtsJob tbTtsJobs = iTbTtsJobService.getById(jobid);if (null == tbTtsJobs) {try {logger.info("未查询到ttsTask任务:type={}, jobid={},放弃执行", type, jobid);cronTaskRegistrar.removeCronTask(CronTaskRegistrar.runnableMap.get(runnableName));CronTaskRegistrar.runnableMap.remove(runnableName);return;}catch (Exception e){e.printStackTrace();return;}}if (tbTtsJobs.getStatus().intValue() == 1 || tbTtsJobs.getExecStatus().intValue() == 1) {// 任务未开启,直接结束,且清除任务cronTaskRegistrar.removeCronTask(CronTaskRegistrar.runnableMap.get(runnableName));CronTaskRegistrar.runnableMap.remove(runnableName);} else {if (tbTtsJobs.getType() == 1 || tbTtsJobs.getType() == 2) {tbTtsJobs.setExecStatus(1);tbTtsJobs.setStatus(1);}iTbTtsJobService.processJob(tbTtsJobs);logger.info("ttsTask任务进入执行队列:type={}, jobid={}", type, jobid);}}}

创建表

CREATE TABLE `tb_tts_job` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',`name` varchar(255) DEFAULT NULL COMMENT '任务名称',`content` text COMMENT '文本内容',`cron` varchar(255) DEFAULT NULL COMMENT '定时标识',`create_time` datetime DEFAULT NULL COMMENT '创建时间',`status` int(1) DEFAULT NULL COMMENT '状态,1-未开启,0-已开启',`exec_status` int(2) DEFAULT NULL COMMENT '执行状态 0-生效 1-已关闭',`type` int(1) DEFAULT NULL COMMENT '任务类型,1-立即执行,2-定时一次,3-每天固定时间执行',`do_time` datetime DEFAULT NULL COMMENT '执行时间',`last_do_time` datetime DEFAULT NULL COMMENT '最近执行时间',`index_type` varchar(1) DEFAULT NULL COMMENT '指令类型',`run_time` varchar(20) DEFAULT NULL COMMENT '时间',`run_date_time` varchar(30) DEFAULT NULL COMMENT '日期',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=34 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='tts语音定时任务';

启动项目加载定时任务

package com.yicheng.web.controller.common;import com.yicheng.business.ttsjob.domain.TbTtsJob;
import com.yicheng.business.ttsjob.service.ITbTtsJobService;
import com.yicheng.business.ttsjob.task.DoTask;
import com.yicheng.common.scheduler.CronTaskRegistrar;
import com.yicheng.common.scheduler.SchedulingRunnable;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;import java.util.List;/*** 项目启动后加载动态定时任务*/
@Slf4j
@Component
public class JobsLoader implements ApplicationRunner {private static final Logger logger = LoggerFactory.getLogger(JobsLoader.class);@Autowiredprivate ITbTtsJobService iTbTtsJobsService;@Autowiredprivate CronTaskRegistrar cronTaskRegistrar;/*** 项目启动后加载动态定时任务* @param args* @throws Exception*/@Overridepublic void run(ApplicationArguments args) throws Exception {ttsTaskLoader();}/*** @description: 加载定时发送任务* @create: 2025/01/10 15:50* @param:* @return:*/public void ttsTaskLoader() {List<TbTtsJob> tbTtsJobs = iTbTtsJobsService.selectTtsJobList();tbTtsJobs.forEach(ttsJob -> {String runnableName = DoTask.jobNamePrefix + ttsJob.getId();SchedulingRunnable task = new SchedulingRunnable(runnableName, "doTask", "ttsSend", runnableName, ttsJob.getType(), ttsJob.getId());cronTaskRegistrar.addCronTask(task, ttsJob.getCron());CronTaskRegistrar.runnableMap.put(runnableName, task);});logger.info("启动加载动态定时任务[Job],共{}条", tbTtsJobs.size());}}

任务的新增、和移除
Controller

import com.yicheng.business.ttsjob.domain.TbTtsJob;
import com.yicheng.business.ttsjob.service.ITbTtsJobService;
import com.yicheng.common.core.page.TableDataInfo;
import com.yicheng.common.core.controller.BaseController;
import com.yicheng.common.core.domain.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;import java.text.ParseException;
import java.util.List;/*** @className: TtsJobsController* @description: 定时指令下发控制器* @creat: 2025/1/10 14:06*/
@RestController
@RequestMapping("/business/ttsJob")
public class TtsJobsController extends BaseController {@Autowiredprivate ITbTtsJobService iTbTtsJobsService;/*** @description: tts定时任务列表* @create: 2025/01/13 11:47* @param:* @return:*/@GetMapping("/list")public TableDataInfo list(TbTtsJob ttsJobs) {startPage();List<TbTtsJob> tbTtsJobs = iTbTtsJobsService.selectTtsJobList(ttsJobs);return getDataTable(tbTtsJobs);}/*** @description: 新增定时任务* @create: 2025/01/10 14:07* @param:* @return:*/@ResponseBody@PostMapping("/addJob")public R<Integer> addJob(@RequestBody TbTtsJob ttsJobs) {try {return R.ok(iTbTtsJobsService.addTtsJob(ttsJobs));} catch (Exception e) {throw new RuntimeException(e);}}/*** @description: 新增定时任务* @create: 2025/01/10 14:07* @param:* @return:*/@ResponseBody@PostMapping("/updateJob")public R<Integer> updateJob(@RequestBody TbTtsJob ttsJobs) {try {return R.ok(iTbTtsJobsService.update(ttsJobs));} catch (Exception e) {throw new RuntimeException(e);}}/*** @description: 启停定时任务* @create: 2025/01/10 14:07* @param:* @return:*/@ResponseBody@PutMapping("/changeStatus")public R changeStatus(@RequestBody TbTtsJob ttsJobs) throws ParseException {return R.ok(iTbTtsJobsService.open(ttsJobs));}/*** @description: 定时任务详情* @create: 2025/01/10 14:07* @param:* @return:*/@GetMapping("/detail/{id}")public R<TbTtsJob> detail(@PathVariable Integer id) {TbTtsJob tbTtsJob= iTbTtsJobsService.detail(id);return R.ok(tbTtsJob);}/*** @description: 删除定时任务* @create: 2025/01/10 14:07* @param:* @return:*/@DeleteMapping("/{id}")public R remove(@PathVariable Integer id) {return R.ok(iTbTtsJobsService.remove(id));}
}

service

import com.baomidou.mybatisplus.extension.service.IService;
import com.yicheng.business.ttsjob.domain.TbTtsJob;
import com.yicheng.business.ttsjob.domain.vo.TtsTaskVo;
import org.springframework.scheduling.annotation.Async;import java.util.List;/*** @className: ITbTtsJobService* @description: tts任务表服务接口* @creat: 2025/1/10 15:05*/
public interface ITbTtsJobService extends IService<TbTtsJob> {/*** @description: tts定时任务列表* @create: 2025/01/13 11:47* @param:* @return:*/List<TbTtsJob> selectTtsJobList(TbTtsJob tbTtsJob);List<TbTtsJob> selectTtsJobList();/*** @description: 新增定时任务* @create: 2025/01/10 14:07* @param:* @return:*/int addTtsJob(TbTtsJob tbTtsJob) throws Exception ;/*** @description: 新增定时任务* @create: 2025/01/10 14:07* @param:* @return:*/int update(TbTtsJob tbTtsJob) throws Exception ;/*** @description: 启停定时任务* @create: 2025/01/10 14:07* @param:* @return:*/int open(TbTtsJob tbTtsJob);/*** @description: 定时任务详情* @create: 2025/01/10 14:07* @param:* @return:*/TbTtsJob detail(Integer id);/*** @description: 删除定时任务* @create: 2025/01/10 14:07* @param:* @return:*/int remove(Integer id);void processJob(TbTtsJob tbTtsJobs);}

impl

package com.yicheng.business.ttsjob.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yicheng.base.service.IVehicleService;
import com.yicheng.business.instruction.domain.vo.textIssuanceRequset;
import com.yicheng.business.instruction.service.IIssuanceSevice;
import com.yicheng.business.ttsjob.domain.TbTtsJob;
import com.yicheng.business.ttsjob.mapper.TbTtsJobMapper;
import com.yicheng.business.ttsjob.service.ITbTtsJobService;
import com.yicheng.business.ttsjob.task.DoTask;
import com.yicheng.common.annotation.DataScope;
import com.yicheng.common.scheduler.CronTaskRegistrar;
import com.yicheng.common.scheduler.SchedulingRunnable;
import com.yicheng.common.utils.DateCronUtil;
import com.yicheng.common.utils.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;import static com.yicheng.common.utils.SecurityUtils.*;/*** tts语音定时任务Service业务层处理** @author yicheng* @date 2025-01-10*/
@Service
public class TbTtsJobServiceImpl extends ServiceImpl<TbTtsJobMapper, TbTtsJob> implements ITbTtsJobService {private static final Logger logger = LoggerFactory.getLogger(TbTtsJobServiceImpl.class);@Autowiredprivate TbTtsJobMapper tbTtsJobMapper;@Autowiredprivate CronTaskRegistrar cronTaskRegistrar;@Autowiredprivate IVehicleService iTbVehicleService;@Autowiredprivate IIssuanceSevice iIssuanceSevice;/*** @description: tts定时任务列表* @create: 2025/01/13 11:47* @param:* @return:*/@Overridepublic List<TbTtsJob> selectTtsJobList(TbTtsJob tbTtsJob) {tbTtsJob.setGroupId(getDeptId());List<TbTtsJob> tbTtsJobs = tbTtsJobMapper.selectTtsJobPageList(tbTtsJob);return tbTtsJobs;}@Overridepublic List<TbTtsJob> selectTtsJobList() {return tbTtsJobMapper.selectTtsJobList();}/*** @description: 新增定时任务* @create: 2025/01/10 14:07* @param:* @return:*/@Overridepublic int addTtsJob(TbTtsJob ttsJobs) throws Exception {SimpleDateFormat sdfDt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");// 任务类型,1-立即执行,2-定时一次,3-每天固定时间执行// 立即执行,默认2分钟后执行if (ttsJobs.getType() == 1) {ttsJobs.setStatus(0);ttsJobs.setExecStatus(0);Calendar beforeTime = Calendar.getInstance();// 30秒之后的时间beforeTime.add(Calendar.SECOND, 30);Date date = beforeTime.getTime();String cron = DateCronUtil.getCron(date);// spring task cron不支持年int index = cron.lastIndexOf(" ");cron = cron.substring(0, index);ttsJobs.setCron(cron);ttsJobs.setDoTime(date);} else if (ttsJobs.getType() == 2) {ttsJobs.setStatus(0);ttsJobs.setExecStatus(0);String oneceTime = ttsJobs.getRunDateTime();Date date = sdfDt.parse(oneceTime);String cron = DateCronUtil.getCron(date);// spring task cron不支持年int index = cron.lastIndexOf(" ");cron = cron.substring(0, index);ttsJobs.setCron(cron);ttsJobs.setDoTime(date);} else {ttsJobs.setStatus(0);ttsJobs.setExecStatus(0);String everyDayTime = ttsJobs.getRunTime();String timeStr = sdf.format(new Date()) + " " + everyDayTime;Date date = sdfDt.parse(timeStr);String cronTmp = DateCronUtil.getCron(date);String[] cronTmpArr = cronTmp.split(" ");String cron = cronTmpArr[0] + " " + cronTmpArr[1] + " " + cronTmpArr[2] + " * * ? *";// spring task cron不支持年int index = cron.lastIndexOf(" ");cron = cron.substring(0, index);ttsJobs.setCron(cron);ttsJobs.setRunTime(timeStr);}int result =tbTtsJobMapper.insert(ttsJobs);String runnableName = DoTask.jobNamePrefix + ttsJobs.getId();SchedulingRunnable task = new SchedulingRunnable(runnableName, "doTask", "ttsSend", runnableName, ttsJobs.getType(), ttsJobs.getId());cronTaskRegistrar.addCronTask(task, ttsJobs.getCron());CronTaskRegistrar.runnableMap.put(runnableName, task);logger.info("AddCronTask is OK!");return result;}@Overridepublic int update(TbTtsJob ttsJobs) throws Exception {SimpleDateFormat sdfDt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");// 任务类型,1-立即执行,2-定时一次,3-每天固定时间执行// 立即执行,默认2分钟后执行if (ttsJobs.getType() == 1) {ttsJobs.setExecStatus(0);Calendar beforeTime = Calendar.getInstance();// 30秒之后的时间beforeTime.add(Calendar.SECOND, 30);Date date = beforeTime.getTime();String cron = DateCronUtil.getCron(date);// spring task cron不支持年int index = cron.lastIndexOf(" ");cron = cron.substring(0, index);ttsJobs.setCron(cron);ttsJobs.setDoTime(date);} else if (ttsJobs.getType() == 2) {ttsJobs.setExecStatus(0);String oneceTime = ttsJobs.getRunDateTime();Date date = sdfDt.parse(oneceTime);String cron = DateCronUtil.getCron(date);// spring task cron不支持年int index = cron.lastIndexOf(" ");cron = cron.substring(0, index);ttsJobs.setCron(cron);ttsJobs.setDoTime(date);} else {ttsJobs.setExecStatus(0);String everyDayTime = ttsJobs.getRunTime();String timeStr = sdf.format(new Date()) + " " + everyDayTime;Date date = sdfDt.parse(timeStr);String cronTmp = DateCronUtil.getCron(date);String[] cronTmpArr = cronTmp.split(" ");String cron = cronTmpArr[0] + " " + cronTmpArr[1] + " " + cronTmpArr[2] + " * * ? *";// spring task cron不支持年int index = cron.lastIndexOf(" ");cron = cron.substring(0, index);ttsJobs.setCron(cron);ttsJobs.setRunTime(timeStr);}if(ttsJobs.getStatus() == 0){String runnableName_del = DoTask.jobNamePrefix + ttsJobs.getId();cronTaskRegistrar.removeCronTask(CronTaskRegistrar.runnableMap.get(runnableName_del));CronTaskRegistrar.runnableMap.remove(runnableName_del);String runnableName = DoTask.jobNamePrefix + ttsJobs.getId();SchedulingRunnable task = new SchedulingRunnable(runnableName, "doTask", "ttsSend", runnableName, ttsJobs.getType(), ttsJobs.getId());cronTaskRegistrar.addCronTask(task, ttsJobs.getCron());CronTaskRegistrar.runnableMap.put(runnableName, task);}ttsJobs.setLastDoTime(null);int result =tbTtsJobMapper.updateById(ttsJobs);return result;}/*** @description: 启停定时任务* @create: 2025/01/10 14:07* @param:* @return:*/@Overridepublic int open(TbTtsJob tbTtsJob) {TbTtsJob updateJob = tbTtsJobMapper.selectById(tbTtsJob.getId());try {if (tbTtsJob.getStatus().intValue() == 0) {String runnableName = DoTask.jobNamePrefix + updateJob.getId();SchedulingRunnable task = new SchedulingRunnable(runnableName, "doTask", "ttsSend", runnableName, updateJob.getType(), updateJob.getId());cronTaskRegistrar.addCronTask(task, updateJob.getCron());CronTaskRegistrar.runnableMap.put(runnableName, task);} else {String runnableName = DoTask.jobNamePrefix + updateJob.getId();cronTaskRegistrar.removeCronTask(CronTaskRegistrar.runnableMap.get(runnableName));CronTaskRegistrar.runnableMap.remove(runnableName);}}catch (Exception e) {logger.error("open error:{}", e);}updateJob.setStatus(tbTtsJob.getStatus());return tbTtsJobMapper.updateById(updateJob);}@Overridepublic TbTtsJob detail(Integer id) {TbTtsJob updateJob = tbTtsJobMapper.selectById(id);return updateJob;}/*** @description: 删除定时任务* @create: 2025/01/10 14:07* @param:* @return:*/@Overridepublic int remove(Integer id) {try {TbTtsJob tbTtsJob = tbTtsJobMapper.selectById(id);String runnableName = DoTask.jobNamePrefix + tbTtsJob.getId();cronTaskRegistrar.removeCronTask(CronTaskRegistrar.runnableMap.get(runnableName));CronTaskRegistrar.runnableMap.remove(runnableName);}catch (Exception e){e.printStackTrace();}return tbTtsJobMapper.deleteById(id);}@Async@Overridepublic void processJob(TbTtsJob tbTtsJobs) {String groupIdsString = tbTtsJobs.getGroupIds();Set<Long> groupIds = Arrays.stream(groupIdsString.split(",")).map(String::trim).map(Long::parseLong).collect(Collectors.toSet());List<String> commNos = iTbVehicleService.getCommNos(groupIds, tbTtsJobs.getVehicleType());textIssuanceRequset textRequset = new textIssuanceRequset();textRequset.setSendType(2);textRequset.setPhoneNo(commNos.stream().collect(Collectors.joining(",")));textRequset.setContent(tbTtsJobs.getContent());textRequset.setMessageType(Arrays.asList(3));tbTtsJobs.setLastDoTime(DateUtils.getNowDate());tbTtsJobMapper.updateById(tbTtsJobs);iIssuanceSevice.sendTextByTaskJob(textRequset);logger.info("Processing task is OK!");}
}

根据日期生成cron

package com.yicheng.common.utils;import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;/*** @className: DateCronUtil* @description: 日期与cron表达式 转换工具类* @author: zhuYaqiang* @creat: 2025/1/10 14:06*/
public class DateCronUtil {/*** 日期转化为cron表达式* @param date* @return*/public static String getCron(Date  date){String dateFormat="ss mm HH dd MM ? yyyy";return  DateCronUtil.fmtDateToStr(date, dateFormat);}/*** cron表达式转为日期* @param cron* @return*/public static Date getCronToDate(String cron) {String dateFormat="ss mm HH dd MM ? yyyy";SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);Date date = null;try {date = sdf.parse(cron);} catch (ParseException e) {return null;}return date;}/*** Description:格式化日期,String字符串转化为Date** @param date* @param dtFormat*            例如:yyyy-MM-dd HH:mm:ss yyyyMMdd* @return*/public static String fmtDateToStr(Date date, String dtFormat) {if (date == null) {return "";}try {SimpleDateFormat dateFormat = new SimpleDateFormat(dtFormat);return dateFormat.format(date);} catch (Exception e) {e.printStackTrace();return "";}}
}

前端页面截图
在这里插入图片描述

在这里插入图片描述

至此:springboot +scheduler+cron 实现动态定时任务 简单实现 , 有需要的可以点赞 收藏哦

相关文章:

SpringBoot 实现动态管理定时任务 Job的动态操作(添加、修改、启停、执行、删除)以及界面展示和具体Job的创建与执行示例

SpringBoot 实现动态管理定时任务 Job的动态操作&#xff08;添加、修改、启停、执行、删除&#xff09;以及界面展示和具体Job的创建与执行示例 关键接口类&#xff1a; CronTaskRegistrar SchedulingRunnable . 添加定时任务注册类&#xff0c;用来增加、删除定时任务 impo…...

FPGA中场战事

2023年10月3日,英特尔宣布由桑德拉里维拉(Sandra Rivera)担任“分拆”后独立运营的可编程事业部首席执行官。 从数据中心和人工智能(DCAI)部门总经理,转身为执掌该业务的CEO,对她取得像AMD掌门人苏姿丰博士类似的成功,无疑抱以厚望。 十年前,英特尔花费167亿美元真金白银…...

_CLASSDEF在C++中的用法详解及示例

_CLASSDEF在C++中的用法详解及示例 _CLASSDEF的定义与使用示例说明代码解析总结在C++编程中,宏(Macro)是一种预处理指令,它允许程序员在编译之前对代码进行文本替换。_CLASSDEF是一个自定义的宏,它提供了一种便捷的方式来定义类及其相关类型。本文将详细介绍_CLASSDEF在C+…...

缓存-Redis-数据结构-redis哪些数据结构是跳表实现的?

在 Redis 中&#xff0c;跳表&#xff08;Skip List&#xff09; 被用于实现 有序集合&#xff08;Sorted Set&#xff09; 数据结构。以下是对此实现的详细解释&#xff1a; Redis中的有序集合&#xff08;Sorted Set&#xff09; 有序集合&#xff08;Sorted Set&#xff0…...

K8S中Service详解(二)

Service类型 Service的资源清单文件&#xff1a; --- kind: Service # 资源类型 apiVersion: v1 # 资源版本 metadata: # 元数据name: service # 资源名称namespace: dev # 命名空间 spec: # 描述selector: # 标签选择器&#xff0c;用于确定当前service代理哪些podapp: ngin…...

鸿蒙模块概念和应用启动相关类(HAP、HAR、HSP、AbilityStage、UIAbility、WindowStage、window)

目录 鸿蒙模块概念 HAP entry feature har shared 使用场景 HAP、HAR、HSP介绍 HAP、HAR、HSP开发 应用的启动 AbilityStage UIAbility WindowStage Window 拉起应用到显示到前台流程 鸿蒙模块概念 HAP hap包是手机安装的最小单元&#xff0c;1个app包含一个或…...

【EXCEL_VBA_实战】多工作薄合并深入理解

工作背景&#xff1a;多个工作薄存在冲突的名称&#xff0c;需快速合并 困难点&#xff1a;工作表移动复制时&#xff0c;若有冲突的名称&#xff0c;会不断弹出对话框待人工确认 思路&#xff1a;利用代码确认弹出的对话框 关键代码&#xff1a;Application.DisplayAlerts …...

Maven的下载安装配置

maven的下载安装配置 maven是什么 Maven 是一个用于 Java 平台的 自动化构建工具&#xff0c;由 Apache 组织提供。它不仅可以用作包管理&#xff0c;还支持项目的开发、打包、测试及部署等一系列行为 Maven的核心功能 项目构建生命周期管理&#xff1a;Maven定义了项目构建…...

网络打印机的搜索与连接(一)

介绍 网络打印机就是可以通过网络连接上的打印机&#xff0c;这类打印机分2种&#xff1a;自身具有互联网接入功能可以分配IP的打印机我们称为网络打印机、另外一种就是被某台电脑连接上去后通过共享的方式共享到网络里面的我们称为共享打印机。现在还有一种可以通过互联网连接…...

高并发压力测试

高并发压力测试 CountDownLatch就是JUC包下的一个工具&#xff0c;整个工具最核心的功能就是计数器。 需要一个并发安全的计数器来操作。CountDownLatch就可以实现。 给CountDownLatch设置一个数值。 每个业务处理完毕之后&#xff0c;执行一次countDown方法&#xff0c;指…...

Python中采用.add_subplot绘制子图的方法简要举例介绍

Python中采用.add_subplot绘制子图的方法简要举例介绍 目录 Python中采用.add_subplot绘制子图的方法简要举例介绍一、Python中绘制子图的方法1.1 add_subplot函数1.2 基本语法&#xff08;1&#xff09;add_subplot的核心语法&#xff08;2&#xff09;add_subplot在中编程中的…...

ipad和macbook同步zotero文献附件失败的解决办法

背景&#xff1a;我所有的文献及其附件pdf都是在台式机&#xff08;windows系统&#xff09;&#xff0c;想要把这些文献同步到云上&#xff0c;然后再从云上同步到平板和其他笔记本电脑比如macbook。文献同步虽已成功&#xff0c;但文献附件都无法打开。 平板报错如下&#xf…...

循环队列(C语言)

从今天开始我会开启一个专栏leetcode每日一题&#xff0c;大家互相交流代码经验&#xff0c;也当作我每天练习的自我回顾。第一天的内容是leetcode622.设计循环队列。 一、题目详细 设计你的循环队列实现。 循环队列是一种线性数据结构&#xff0c;其操作表现基于 FIFO&#…...

Amazon Redshift实用命令语句

1. 数据库管理相关命令 创建数据库 CREATE DATABASE mydatabase;Amazon Redshift创建数据库命令除了基本形式外&#xff0c;还有以下几种带不同参数的形式&#xff1a; 带OWNER参数 可以指定数据库的所有者&#xff0c;通常是一个数据库用户或角色。 CREATE DATABASE myda…...

音频入门(二):音频数据增强

本文介绍了一些常见的音频数据增强方法&#xff0c;并给出了代码实现。 目录 一、简介 二、代码 1. 安装必要的库 2. 代码 3. 各函数的介绍 4. 使用方法 参考&#xff1a; 一、简介 音频数据增强是机器学习和深度学习领域中用于改善模型性能和泛化能力的技术。 使用数据…...

【Hadoop面试题2025】

文章目录 简单题故障及相应的处理方法中等难度高难度小文件小文件的产生小文件问题的影响小文件治理方案推荐方案 冷文件冷文件的产生冷文件问题的影响冷文件治理方案推荐方案 简单题 一、基础概念类 什么是Hadoop&#xff1f; 答案&#xff1a;Hadoop是一个开源的分布式计算框…...

Unity自学之旅03

Unity自学之旅03 Unity自学之旅03&#x1f4dd; 碰撞体 Collider 基础定义与作用常见类型OnCollisionEnter 事件碰撞触发器 &#x1f917; 总结归纳 Unity自学之旅03 &#x1f4dd; 碰撞体 Collider 基础 定义与作用 定义&#xff1a;碰撞体是游戏中用于检测物体之间碰撞的组…...

STranslate 中文绿色版即时翻译/ OCR 工具 v1.3.1.120

STranslate 是一款功能强大且用户友好的翻译工具&#xff0c;它支持多种语言的即时翻译&#xff0c;提供丰富的翻译功能和便捷的使用体验。STranslate 特别适合需要频繁进行多语言交流的个人用户、商务人士和翻译工作者。 软件功能 1. 即时翻译&#xff1a; 文本翻译&#xff…...

树的存储(c++)

树结构相对线性结构来说就⽐较复杂。存储时&#xff0c;既要保存值域&#xff0c;也要保存结点与结点之间的关系。实际中树有很多种存储⽅式&#xff1a;双亲表⽰法&#xff0c;孩⼦表⽰法、孩⼦双亲表⽰法以及孩⼦兄弟表⽰法等。现阶段&#xff0c;我们只⽤掌握孩⼦表⽰法&…...

JVM面试题解,垃圾回收之“对象存活判断”剖析

一、JVM怎么判断一个类/对象是不是垃圾&#xff1f; 先来说如何判断一个对象是不是垃圾 最常用的就是引用计数法和可达性分析 引用计数法 引用计数法为每个对象维护一个计数器来跟踪有多少个引用指向该对象。每当创建一个新的引用指向某个对象时&#xff0c;计数器加1&…...

【Elasticsearch】 Ingest Pipeline `processors`属性详解

在Elasticsearch中&#xff0c;Ingest Pipeline 的 processors 属性是一个数组&#xff0c;包含一个或多个处理器&#xff08;processors&#xff09;。每个处理器定义了一个数据处理步骤&#xff0c;可以在数据索引之前对数据进行预处理或富化。以下是对 processors 属性中常见…...

Springboot3 自动装配之核心文件:imports文件

注&#xff1a;本文以spring-boot v3.4.1源码为基础&#xff0c;梳理spring-boot应用启动流程、分析自动装配的原理 如果对spring-boot2自动装配有兴趣&#xff0c;可以看看我另一篇文章&#xff1a; Springboot2 自动装配之spring-autoconfigure-metadata.properties和spring…...

Ceisum无人机巡检直播视频投射

接上次的视频投影&#xff0c;Leader告诉我这个视频投影要用在两个地方&#xff0c;一个是我原先写的轨迹回放那里&#xff0c;另一个在无人机起飞后的地图回显&#xff0c;要实时播放无人机拍摄的视频&#xff0c;还要能转镜头&#xff0c;让我把这个也接一下。 我的天&#x…...

【IJCAI】2025 投稿重点记录

【IJCAI】2025 投稿重点记录 写在最前面【IJCAI】2025 投稿重点记录1. 文件说明2. 论文长度要求正式版本的页面扩展 3. 作者信息及匿名性要求4. 摘要5. 附录与补充内容6. 审稿重点与伦理声明7. 参考文献与贡献声明8. 技术要点与补充细节 &#x1f308;你好呀&#xff01;我是 是…...

U3D的.Net学习

Mono&#xff1a;这是 Unity 最初采用的方式&#xff0c;它将 C# 代码编译为中间语言 (IL)&#xff0c;然后在目标平台上使用虚拟机 (VM) 将其转换为本地机器码执行。 IL2CPP&#xff1a;这是一种较新的方法&#xff0c;它会将 C# 代码先编译为 C 代码&#xff0c;再由 C 编译器…...

C++ 二叉搜索树

目录 概念 性能分析 二叉搜索树的插入 二叉树的查找 二叉树的前序遍历 二叉搜索树的删除&#xff08;重点&#xff09; 完整代码 key与value的使用 概念 对于一个二叉搜索树 若它的左子树不为空&#xff0c;则左子树上所有的节点的值都小于等于根节点的值若它的右子树不为空…...

使用HTML5 Canvas 实现呼吸粒子球动画效果的原理

在网页开发领域&#xff0c;动画效果能够极大地提升用户体验&#xff0c;让页面变得更加生动有趣。今天&#xff0c;我们深入剖析一个基于 HTML5 Canvas 的 3D 粒子动画 —— 呼吸粒子球。通过详细解读其代码实现&#xff0c;我们将全面了解如何运用 HTML5 的强大功能构建出如此…...

计算机网络 (56)交互式音频/视频

一、定义与特点 定义&#xff1a;交互式音频/视频是指用户使用互联网和其他人进行实时交互式通信的技术&#xff0c;包括语音、视频图像等多媒体实时通信。 特点&#xff1a; 实时性&#xff1a;音频和视频数据是实时传输和播放的&#xff0c;用户之间可以进行即时的交流。交互…...

Androidstudio 中,project下的.gitignore和module下的.gitignore有什么区别,生效优先级是什么

在 Android Studio 项目中&#xff0c;project 根目录下的 .gitignore 文件和 module 目录下的 .gitignore 文件作用和生效优先级是不同的&#xff0c;理解它们之间的区别非常重要&#xff0c;可以避免不必要的提交和冲突。 1. project 根目录下的 .gitignore&#xff1a; 作…...

三篇物联网漏洞挖掘综述

由于物联网设备存在硬件资源受限、硬件复杂异构&#xff0c; 代码、文档未公开的问题&#xff0c; 物联网设备的漏洞挖掘存在较大的挑战&#xff1a; 硬件资源受限性: 通用动态二进分析技术需要在运行程序外围实施监控分析。由于物联网设备存储资源(存储)的受限性&#xff0c;…...

【动态规划】落花人独立,微雨燕双飞 - 8. 01背包问题

本篇博客给大家带来的是01背包问题之动态规划解法技巧. &#x1f40e;文章专栏: 动态规划 &#x1f680;若有问题 评论区见 ❤ 欢迎大家点赞 评论 收藏 分享 如果你不知道分享给谁,那就分享给薯条. 你们的支持是我不断创作的动力 . 王子,公主请阅&#x1f680; 要开心要快乐顺便…...

uniapps使用HTML5的io模块拷贝文件目录

最近在集成sqlite到uniapp的过程中&#xff0c;因为要将sqlite数据库预加载&#xff0c;所以需要使用HTML5的plus.io模块。使用过程中遇到了许多问题&#xff0c;比如文件路径总是解析不到等。尤其是应用私有文档目录’_doc’。 根据官方文档&#xff1a; 为了安全管理应用的…...

Word2Vec如何优化从中间层到输出层的计算?

文章目录 Word2Vec如何优化从中间层到输出层的计算&#xff1f;用负采样优化中间层到输出层的计算负采样方法的关键思想负采样的例子负采样的采样方法 Word2Vec如何优化从中间层到输出层的计算&#xff1f; 重要性&#xff1a;★★ 用负采样优化中间层到输出层的计算 以词汇…...

C#中的语句

C#提供了各式各样的语句&#xff0c;大多数是由C和C发展而来&#xff0c;当然&#xff0c;在C#中做了相应修改。语句和表达式一样&#xff0c;都是C#程序的基本组成部分&#xff0c;在本文我们来一起学习C#语句。 1.语句 语句是构造所有C#程序的过程构造块。在语句中可以声明…...

2.3.1(项目)kv存储——框架梳理(待定)

一、过一遍代码路线&#xff1a; 体会&#xff1a;&#xff08;1&#xff09;接口统一、测试标准统一&#xff0c;软件才会有量产的过程&#xff1b;&#xff08;b&#xff09;多层框架&#xff0c;实现业务部分和网络部分的完全剥离。 实现多层框架&#xff1a; &#xff0…...

【YOLOv10改进[Backbone]】使用ConvNeXtV2替换Backbone

本文将进行在YOLOv10中使用ConvNeXtV2替换Backbone魔改v10的实践,文中含全部代码、详细修改方式。助您轻松理解改进的方法。 目录 一 ConvNeXtV2 二 魔改YOLOv10 1 整体修改 ① 添加python文件 ② 修改ultralytics/nn/tasks.py文件 2 配置文件...

在C#中添加I/O延时和持续时间

在C#中添加I/O延时和持续时间&#xff0c;可以通过以下方法实现。具体来说&#xff0c;延时可以通过Thread.Sleep、Task.Delay等方式来模拟延迟&#xff0c;而持续时间的控制可以通过循环结构来设定持续的时间。在执行I/O操作时&#xff0c;你可以在操作之间添加延时&#xff0…...

VUE之路由Props、replace、编程式路由导航、重定向

目录 1、路由_props的配置 2、路由_replaces属性 3、编程式路由导航 4、路由重定向 1、路由_props的配置 1&#xff09;第一种写法&#xff0c;将路由收到的所有params参数作为props传给路由组件 只能适用于params参数 // 创建一个路由器&#xff0c;并暴露出去// 第一步…...

RabbitMQ的消息可靠性保证

文章目录 1.环境搭建1.common-rabbitmq-starter 配置防止消费者抢消息&#xff08;基础配置&#xff09;2.common-rabbitmq-starter-demo下创建一个生产者一个消费者 2.生产者可靠性1.开启消息超时重试机制2.生产者开启ConfirmCallback消息确认机制1.application.yml2.TestConf…...

MySQL 很重要的库 - 信息字典

在做owasp SQL 注入的时候&#xff0c;有个很重要的库&#xff0c;那就是 信息库: 这个库就是: information_schema; &#xff08;准确的说&#xff0c;数据字典) mysql> show databases; -------------------- | Database | -------------------- | informa…...

使用C#对指定的MYSQL数据库进行备份以及常见问题

最近在开发过程中&#xff0c;需要做个MYSQL数据库的备份&#xff0c;大致总结了一下代码&#xff0c;以及常见的坑 string bakName "database" DateTime.Now.ToString("yyyyMMddHHmmss") ".sql";//备份后的数据库文件名var bakupFilePath &q…...

Appium(四)

一、app页面元素定位 1、通过id定位元素: resrouce-id2、通过ClassName定位&#xff1a;classname3、通过AccessibilityId定位&#xff1a;content-desc4、通过AndroidUiAutomator定位5、通过xpath定位xpath、id、class、accessibility id、android uiautomatorUI AutomatorUI自…...

jvm_threads_live_threads 和 jvm_threads_states_threads 这两个指标之间存在一定的关系,但它们关注的维度不同

jvm_threads_live_threads 和 jvm_threads_states_threads 这两个指标之间存在一定的关系&#xff0c;但它们关注的维度不同。以下是它们的详细关系和区别&#xff1a; 1. jvm_threads_live_threads 含义&#xff1a; 表示当前 JVM 中存活的线程总数&#xff08;即当前活动的线…...

docker 部署.netcore应用优势在什么地方?

目录 1. 环境一致性 2. 简化依赖管理 3. 快速部署与扩展 4. 资源利用率高 5. 版本控制与回滚 6. 安全性 7. 生态系统支持 8. 微服务架构支持 9. 降低成本 10. 开发体验提升 总结 使用 Docker 部署 .NET Core 应用有许多优势&#xff0c;特别是在开发、测试和生产环境…...

SpringBoot开发(一)应用jar包

1. SpringBoot开发 1.1. 目标及简介 1.1.1. 目标 &#xff08;1&#xff09;掌握微服务SpringBoot在实际项目开发中常用的核心技术栈及其在典型业务场景下的应用实战。   &#xff08;2&#xff09;掌握SpringBoot SpringMVC Mybatis在Java Web应用开发过程的技术干货以及…...

【Linux】深刻理解动静态库

1.什么是库 库是写好的现有的&#xff0c;成熟的&#xff0c;可以复⽤的代码。现实中每个程序都要依赖很多基础的底层库&#xff0c;不可能每个⼈的代码都从零开始&#xff0c;因此库的存在意义⾮同寻常。本质上来说库是⼀种可执⾏代码的⼆进制形式&#xff0c;可以被操作系统载…...

【spring 事务】事务的基本使用,事务隔离级别、事务传播机制

在Spring框架中&#xff0c;声明式事务管理是一种通过注解或配置文件自动管理事务的方式&#xff0c;而不需要手动编写事务管理代码。Transactional是Spring提供的一个注解&#xff0c;用于声明式事务管理&#xff0c;它使得事务的管理变得简单而清晰。 主要特性 自动事务管理…...

arkime 和elasticsearch安装方法二

这次试一下新的办法 先下载centOS 7 然后改成阿里云镜像 输入命令备份官方yum源配置文件 cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak 下载阿里云源配置&#xff0c;覆盖原文件 curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirr…...

GitCode 助力 AutoTable:共创 MyBatis 生态的自动表格管理新篇章

项目仓库https://gitcode.com/dromara/auto-table 解放双手&#xff0c;专注业务&#xff1a;MyBatis 生态的“自动表格”创新 AutoTable 是一款致力于为 MyBatis 生态赋予“自动表格”功能的创新插件。其核心理念是通过 Java 实体类自动生成和维护数据库的表结构&#xff0c…...

日历热力图,月度数据可视化图表(日活跃图、格子图)vue组件

日历热力图&#xff0c;月度数据可视化图表&#xff0c;vue组件 先看效果&#x1f447; 在线体验https://www.guetzjb.cn/calanderViewGraph/ 日历图简单划分为近一年时间&#xff0c;开始时间是 上一年的今天&#xff0c;例如2024/01/01 —— 2025/01/01&#xff0c;跨度刚…...