0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

SpringBoot如何实现动态增删启停定时任务

Android编程精选 来源:简书 作者:jessehua 2021-09-24 09:49 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

在spring boot项目中,可以通过@EnableScheduling注解和@Scheduled注解实现定时任务,也可以通过SchedulingConfigurer接口来实现定时任务。但是这两种方式不能动态添加、删除、启动、停止任务。

要实现动态增删启停定时任务功能,比较广泛的做法是集成Quartz框架。但是本人的开发原则是:在满足项目需求的情况下,尽量少的依赖其它框架,避免项目过于臃肿和复杂。

查看spring-context这个jar包中org.springframework.scheduling.ScheduledTaskRegistrar这个类的源代码,发现可以通过改造这个类就能实现动态增删启停定时任务功能。

e8f20370-1085-11ec-8fb8-12bb97331649.jpg定时任务列表页e90122f6-1085-11ec-8fb8-12bb97331649.jpg定时任务执行日志

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

@Configuration
publicclassSchedulingConfig{
@Bean
publicTaskSchedulertaskScheduler(){
ThreadPoolTaskSchedulertaskScheduler=newThreadPoolTaskScheduler();
//定时任务执行线程池核心线程数
taskScheduler.setPoolSize(4);
taskScheduler.setRemoveOnCancelPolicy(true);
taskScheduler.setThreadNamePrefix("TaskSchedulerThreadPool-");
returntaskScheduler;
}
}

添加ScheduledFuture的包装类。ScheduledFuture是ScheduledExecutorService定时任务线程池的执行结果。

publicfinalclassScheduledTask{

volatileScheduledFuturefuture;

/**
*取消定时任务
*/
publicvoidcancel(){
ScheduledFuturefuture=this.future;
if(future!=null){
future.cancel(true);
}
}
}

添加Runnable接口实现类,被定时任务线程池调用,用来执行指定bean里面的方法。

publicclassSchedulingRunnableimplementsRunnable{

privatestaticfinalLoggerlogger=LoggerFactory.getLogger(SchedulingRunnable.class);

privateStringbeanName;

privateStringmethodName;

privateStringparams;

publicSchedulingRunnable(StringbeanName,StringmethodName){
this(beanName,methodName,null);
}

publicSchedulingRunnable(StringbeanName,StringmethodName,Stringparams){
this.beanName=beanName;
this.methodName=methodName;
this.params=params;
}

@Override
publicvoidrun(){
logger.info("定时任务开始执行- bean:{},方法:{},参数:{}",beanName,methodName,params);
longstartTime=System.currentTimeMillis();

try{
Objecttarget=SpringContextUtils.getBean(beanName);

Methodmethod=null;
if(StringUtils.isNotEmpty(params)){
method=target.getClass().getDeclaredMethod(methodName,String.class);
}else{
method=target.getClass().getDeclaredMethod(methodName);
}

ReflectionUtils.makeAccessible(method);
if(StringUtils.isNotEmpty(params)){
method.invoke(target,params);
}else{
method.invoke(target);
}
}catch(Exceptionex){
logger.error(String.format("定时任务执行异常- bean:%s,方法:%s,参数:%s ",beanName,methodName,params),ex);
}

longtimes=System.currentTimeMillis()-startTime;
logger.info("定时任务执行结束- bean:{},方法:{},参数:{},耗时:{}毫秒",beanName,methodName,params,times);
}

@Override
publicbooleanequals(Objecto){
if(this==o)returntrue;
if(o==null||getClass()!=o.getClass())returnfalse;
SchedulingRunnablethat=(SchedulingRunnable)o;
if(params==null){
returnbeanName.equals(that.beanName)&&
methodName.equals(that.methodName)&&
that.params==null;
}

returnbeanName.equals(that.beanName)&&
methodName.equals(that.methodName)&&
params.equals(that.params);
}

@Override
publicinthashCode(){
if(params==null){
returnObjects.hash(beanName,methodName);
}

returnObjects.hash(beanName,methodName,params);
}
}

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

@Component
publicclassCronTaskRegistrarimplementsDisposableBean{

privatefinalMapscheduledTasks=newConcurrentHashMap<>(16);

@Autowired
privateTaskSchedulertaskScheduler;

publicTaskSchedulergetScheduler(){
returnthis.taskScheduler;
}

publicvoidaddCronTask(Runnabletask,StringcronExpression){
addCronTask(newCronTask(task,cronExpression));
}

publicvoidaddCronTask(CronTaskcronTask){
if(cronTask!=null){
Runnabletask=cronTask.getRunnable();
if(this.scheduledTasks.containsKey(task)){
removeCronTask(task);
}

this.scheduledTasks.put(task,scheduleCronTask(cronTask));
}
}

publicvoidremoveCronTask(Runnabletask){
ScheduledTaskscheduledTask=this.scheduledTasks.remove(task);
if(scheduledTask!=null)
scheduledTask.cancel();
}

publicScheduledTaskscheduleCronTask(CronTaskcronTask){
ScheduledTaskscheduledTask=newScheduledTask();
scheduledTask.future=this.taskScheduler.schedule(cronTask.getRunnable(),cronTask.getTrigger());

returnscheduledTask;
}


@Override
publicvoiddestroy(){
for(ScheduledTasktask:this.scheduledTasks.values()){
task.cancel();
}

this.scheduledTasks.clear();
}
}

添加定时任务示例类

@Component("demoTask")
publicclassDemoTask{
publicvoidtaskWithParams(Stringparams){
System.out.println("执行有参示例任务:"+params);
}

publicvoidtaskNoParams(){
System.out.println("执行无参示例任务");
}
}

定时任务数据库表设计

添加定时任务实体类

publicclassSysJobPO{
/**
*任务ID
*/
privateIntegerjobId;
/**
*bean名称
*/
privateStringbeanName;
/**
*方法名称
*/
privateStringmethodName;
/**
*方法参数
*/
privateStringmethodParams;
/**
*cron表达式
*/
privateStringcronExpression;
/**
*状态(1正常0暂停)
*/
privateIntegerjobStatus;
/**
*备注
*/
privateStringremark;
/**
*创建时间
*/
privateDatecreateTime;
/**
*更新时间
*/
privateDateupdateTime;

publicIntegergetJobId(){
returnjobId;
}

publicvoidsetJobId(IntegerjobId){
this.jobId=jobId;
}

publicStringgetBeanName(){
returnbeanName;
}

publicvoidsetBeanName(StringbeanName){
this.beanName=beanName;
}

publicStringgetMethodName(){
returnmethodName;
}

publicvoidsetMethodName(StringmethodName){
this.methodName=methodName;
}

publicStringgetMethodParams(){
returnmethodParams;
}

publicvoidsetMethodParams(StringmethodParams){
this.methodParams=methodParams;
}

publicStringgetCronExpression(){
returncronExpression;
}

publicvoidsetCronExpression(StringcronExpression){
this.cronExpression=cronExpression;
}

publicIntegergetJobStatus(){
returnjobStatus;
}

publicvoidsetJobStatus(IntegerjobStatus){
this.jobStatus=jobStatus;
}

publicStringgetRemark(){
returnremark;
}

publicvoidsetRemark(Stringremark){
this.remark=remark;
}

publicDategetCreateTime(){
returncreateTime;
}

publicvoidsetCreateTime(DatecreateTime){
this.createTime=createTime;
}

publicDategetUpdateTime(){
returnupdateTime;
}

publicvoidsetUpdateTime(DateupdateTime){
this.updateTime=updateTime;
}

}
booleansuccess=sysJobRepository.addSysJob(sysJob);
if(!success)
returnOperationResUtils.fail("新增失败");
else{
if(sysJob.getJobStatus().equals(SysJobStatus.NORMAL.ordinal())){
SchedulingRunnabletask=newSchedulingRunnable(sysJob.getBeanName(),sysJob.getMethodName(),sysJob.getMethodParams());
cronTaskRegistrar.addCronTask(task,sysJob.getCronExpression());
}
}

returnOperationResUtils.success();

修改定时任务,先移除原来的任务,再启动新任务

booleansuccess=sysJobRepository.editSysJob(sysJob);
if(!success)
returnOperationResUtils.fail("编辑失败");
else{
//先移除再添加
if(existedSysJob.getJobStatus().equals(SysJobStatus.NORMAL.ordinal())){
SchedulingRunnabletask=newSchedulingRunnable(existedSysJob.getBeanName(),existedSysJob.getMethodName(),existedSysJob.getMethodParams());
cronTaskRegistrar.removeCronTask(task);
}

if(sysJob.getJobStatus().equals(SysJobStatus.NORMAL.ordinal())){
SchedulingRunnabletask=newSchedulingRunnable(sysJob.getBeanName(),sysJob.getMethodName(),sysJob.getMethodParams());
cronTaskRegistrar.addCronTask(task,sysJob.getCronExpression());
}
}

returnOperationResUtils.success();

删除定时任务

booleansuccess=sysJobRepository.deleteSysJobById(req.getJobId());
if(!success)
returnOperationResUtils.fail("删除失败");
else{
if(existedSysJob.getJobStatus().equals(SysJobStatus.NORMAL.ordinal())){
SchedulingRunnabletask=newSchedulingRunnable(existedSysJob.getBeanName(),existedSysJob.getMethodName(),existedSysJob.getMethodParams());
cronTaskRegistrar.removeCronTask(task);
}
}

returnOperationResUtils.success();

定时任务启动/停止状态切换

if(existedSysJob.getJobStatus().equals(SysJobStatus.NORMAL.ordinal())){
SchedulingRunnabletask=newSchedulingRunnable(existedSysJob.getBeanName(),existedSysJob.getMethodName(),existedSysJob.getMethodParams());
cronTaskRegistrar.addCronTask(task,existedSysJob.getCronExpression());
}else{
SchedulingRunnabletask=newSchedulingRunnable(existedSysJob.getBeanName(),existedSysJob.getMethodName(),existedSysJob.getMethodParams());
cronTaskRegistrar.removeCronTask(task);
}

添加实现了CommandLineRunner接口的SysJobRunner类,当spring boot项目启动完成后,加载数据库里状态为正常的定时任务。

@Service
publicclassSysJobRunnerimplementsCommandLineRunner{

privatestaticfinalLoggerlogger=LoggerFactory.getLogger(SysJobRunner.class);

@Autowired
privateISysJobRepositorysysJobRepository;

@Autowired
privateCronTaskRegistrarcronTaskRegistrar;

@Override
publicvoidrun(String...args){
//初始加载数据库里状态为正常的定时任务
ListjobList=sysJobRepository.getSysJobListByStatus(SysJobStatus.NORMAL.ordinal());
if(CollectionUtils.isNotEmpty(jobList)){
for(SysJobPOjob:jobList){
SchedulingRunnabletask=newSchedulingRunnable(job.getBeanName(),job.getMethodName(),job.getMethodParams());
cronTaskRegistrar.addCronTask(task,job.getCronExpression());
}

logger.info("定时任务已加载完毕...");
}
}
}

工具类SpringContextUtils,用来从spring容器里获取bean

@Component
publicclassSpringContextUtilsimplementsApplicationContextAware{

privatestaticApplicationContextapplicationContext;

@Override
publicvoidsetApplicationContext(ApplicationContextapplicationContext)
throwsBeansException{
SpringContextUtils.applicationContext=applicationContext;
}

publicstaticObjectgetBean(Stringname){
returnapplicationContext.getBean(name);
}

publicstaticTgetBean(ClassrequiredType){
returnapplicationContext.getBean(requiredType);
}

publicstaticTgetBean(Stringname,ClassrequiredType){
returnapplicationContext.getBean(name,requiredType);
}

publicstaticbooleancontainsBean(Stringname){
returnapplicationContext.containsBean(name);
}

publicstaticbooleanisSingleton(Stringname){
returnapplicationContext.isSingleton(name);
}

publicstaticClassgetType(Stringname){
returnapplicationContext.getType(name);
}
}

本文完,参考本文代码可成功运行,亲测!

(感谢阅读,希望对你所有帮助)来源:www.jianshu.com/p/0f68936393fd
编辑:jq
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 源代码
    +关注

    关注

    96

    文章

    2953

    浏览量

    70858
  • spring
    +关注

    关注

    0

    文章

    345

    浏览量

    16088
  • Boot
    +关注

    关注

    0

    文章

    154

    浏览量

    37977
  • SpringBoot
    +关注

    关注

    0

    文章

    179

    浏览量

    729

原文标题:告别硬编码,SpringBoot实现动态增删启停定时任务

文章出处:【微信号:AndroidPush,微信公众号:Android编程精选】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    智能获“动态拓扑地图”调度专利:实现医院物流机器人多级优先级动态抢占

    ,鲸智能此次专利的核心突破在于:让调度系统升级为 “会学习、可持续进化的大脑” ,为智慧医院在复杂动态环境下的全院物资流转提供了具备高并发、强鲁棒性的大规模集群解决方案。 医院物流调度的核心痛点:多源任务并发
    的头像 发表于 04-30 13:58 186次阅读
    鲸<b class='flag-5'>启</b>智能获“<b class='flag-5'>动态</b>拓扑地图”调度专利:<b class='flag-5'>实现</b>医院物流机器人多级优先级<b class='flag-5'>动态</b>抢占

    【正点原子PD42S1电机试用】二 电机转了:通过IO模式启动电机

    开箱后,大致了解了PD42S的情况,又浏览了用户手册,决定先将电机驱动套件的运行模式设置成最简单的IO模式,让电机转起来。 1 搭建简单的扩展板 为了便于后续工作的进行,先搭建了一个简单的扩展
    发表于 01-27 21:36

    ProfiNet转CAN智能网关实现西门子PLC对多台空压机自动集中控制

    、能耗偏高的问题,且无法接入车间现有控制系统实现集中监控。为解决上述问题,企业计划搭建集成控制系统,通过西门子1200PLC实现对所有空压机的集中控制与自动调度,核心需求是解决西门
    的头像 发表于 12-29 09:30 842次阅读

    电能质量在线监测装置高温下风扇自动吗?

    配备温控风扇的电能质量在线监测装置在高温下会自动 ,而部分低功耗、全密封或自然散热设计的装置则无此功能。这一功能的核心是通过内置温度传感器监测内部温度,按预设阈值自动控制风扇运行状态。 一、风扇
    的头像 发表于 12-12 15:12 606次阅读
    电能质量在线监测装置高温下风扇自动<b class='flag-5'>启</b><b class='flag-5'>停</b>吗?

    新能源汽车好搭档:超级电容助力系统,节能又降噪

    超级电容作为新能源汽车系统的理想搭档,凭借其瞬时大电流输出、宽温域适应性、长循环寿命及高效能量回收能力,显著提升系统节能性与降噪效果。
    的头像 发表于 12-10 17:20 1370次阅读

    汽车级非同步升压控制器NCV8877:设计与应用全解析

    在汽车电子领域,随着技术的广泛应用,对能够在电池电压下降时提供稳定输出电压的升压控制器的需求日益增长。今天我们就来深入探讨一下安森美(onsemi)的NCV8877汽车级非同步
    的头像 发表于 12-03 16:15 2073次阅读
    汽车级<b class='flag-5'>启</b><b class='flag-5'>停</b>非同步升压控制器NCV8877:设计与应用全解析

    高频设备电源:超级电容稳供动力,延长设备使用寿命

    超级电容凭借毫秒级响应、超长循环寿命和宽温域适应性,成为高频设备的理想电源,可显著提升设备运行稳定性并延长使用寿命 。其核心优势与技术价值体现在以下方面: 一、毫秒级响应:瞬时功率支撑,保障设备
    的头像 发表于 12-02 14:24 956次阅读

    车规铝电解电容:缓解汽车系统供电波动的实用组件

    车规铝电解电容是缓解汽车系统供电波动的核心组件,其通过材料创新、结构优化和智能控制,有效解决了停过程中电压骤降、电流冲击和电磁干扰等关键问题,显著提升了系统可靠性和用户体验。以下从技术
    的头像 发表于 10-17 15:34 619次阅读

    求一个multisim驱动电路的电路仿真

    我需要一个驱动电路的电路仿真,我希望可以把文件直接发给我,用的元器件也告诉我,感谢???。
    发表于 10-11 10:37

    御控物联网远程控制水泵智能自控解决方案

    预测和故障自愈;3)数据可视化分析,优化维护周期。系统采用工业级硬件和加密传输,适用于农业灌溉、城市排水和工业供水等场景,可降低能耗40%,减少维护成本80万元/年,实现精准用水和防涝预警。
    的头像 发表于 09-08 10:48 906次阅读

    Crontab定时任务完全指南

    在凌晨3点,当大多数人还在熟睡时,一位运维工程师的手机突然响起——线上数据库备份失败了。他匆忙起床,打开电脑,手动执行备份脚本,整个过程耗时2小时。这样的场景,在我刚入行时经常遇到。直到我真正掌握了crontab定时任务,才彻底摆脱了"人肉运维"的窘境。
    的头像 发表于 09-05 10:03 1102次阅读

    Task任务:LuatOS实现任务级并发”的核心引擎

    Task任务通过其强大的并发处理能力,使LuatOS能够在单线程环境中模拟多线程执行,通过协程的挂起与恢复机制,实现任务级的并行操作,显著提升系统效能。 sys核心库是LuatOS运行框架库,也是
    的头像 发表于 08-28 13:49 685次阅读
    Task<b class='flag-5'>任务</b>:LuatOS<b class='flag-5'>实现</b>“<b class='flag-5'>任务</b>级并发”的核心引擎

    使用C#实现西门子PLC数据定时读取保存

    在平时开发中,我们时常会遇到需要后台静默运行的应用场景,这些程序不需要用户的直接操作或界面展示,而是专注于定时任务的执行。比如说,我们需要定期从西门子PLC(可编程逻辑控制器)中读取数据并进行保存,以便后续分析使用。
    的头像 发表于 08-07 16:17 2682次阅读
    使用C#<b class='flag-5'>实现</b>西门子PLC数据<b class='flag-5'>定时</b>读取保存

    冠坤台系电容 —— 汽车装置的 “能量小助手”

    电容的创新应用延伸至汽车系统这一特殊场景,以“能量小助手”的角色助力现代汽车实现更高效的能源管理。 **系统的能量挑战与电容的技术突
    的头像 发表于 08-04 17:02 1096次阅读

    智能时代,DF MAX停电池“蓄势待发”

    随着城市交通拥堵日益加剧,系统的重要性正变得前所未有的突出。据相关统计,北京市中心区的平均拥堵率已达 43.6%,而上海更是高达 46.1%。在这种“慢走快”的道路环境中,车辆频繁在红绿灯前
    的头像 发表于 06-05 13:29 653次阅读