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

    文章

    2948

    浏览量

    67190
  • spring
    +关注

    关注

    0

    文章

    340

    浏览量

    14573
  • Boot
    +关注

    关注

    0

    文章

    150

    浏览量

    36121
  • SpringBoot
    +关注

    关注

    0

    文章

    175

    浏览量

    217

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

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

收藏 人收藏

    评论

    相关推荐

    Linux计划任务介绍

    1.计划任务定时任务)基本概述 1.什么是crond crond就是计划任务,类似于我们平时生活中的闹钟。定点执行。 2.为什么要使用crond crond主要是做一些周期性的任务
    的头像 发表于 11-24 15:49 454次阅读

    定时器技术:Air780E如何革新定时任务管理?

    今天讲的是关于Air780E如何革新定时任务管理的内容,希望大家有所收获。
    的头像 发表于 11-07 13:50 411次阅读
    <b class='flag-5'>定时</b>器技术:Air780E如何革新<b class='flag-5'>定时任务</b>管理?

    mysql定时备份任务

    在生产环境上,为了避免数据的丢失,通常情况下都会定时的对数据库进行备份。而Linux的crontab指令则可以帮助我们实现对数据库定时进行备份。首先我们来简单了解crontab指令,如果你会了请跳到下一个内容mysql备份。
    的头像 发表于 10-31 10:07 279次阅读

    PL5501(汽车系统、工业PC电源、USBPD快充、车载充电器、HUB等产品)

    合,为大功率Type-CPD车载充电器提供较优解决方案。 该芯片搭配外置MOS管,能够实现较大200W升降压变换控制。 可适用于汽车系统、工业PC电源、USBPD快充、车载充电器、HUB等产品领域
    发表于 10-16 15:58

    拉板小车限位开关MN8-M18G12-POD1-30-Y技术特性

    拉板小车限位开关的技术特性主要体现在其基本功能与作用、工作原理、技术特点以及维护与检查等方面。这些特性共同确保了限位开关在自动化设备中的稳定运行和可靠性能。
    的头像 发表于 09-14 14:25 428次阅读

    变频器外接按钮如何接线

    以下是变频器外接按钮接线的一般步骤: 确定变频器的型号和规格,查阅其技术手册以获取接线图和接线说明。 准备所需的工具和材料,包括螺丝刀、剥线钳、接线端子、接线盒等。 关闭电源并断开变频器与电源
    的头像 发表于 08-25 10:49 2387次阅读

    linux定时任务的用法总结

    习惯了使用 windows 的计划任务,使用 linux 中的 crontab 管理定时任务时很不适应。
    的头像 发表于 08-14 18:16 955次阅读
    linux<b class='flag-5'>定时任务</b>的用法总结

    ESP8266如何实现时间小于3us的定时任务

    实现一个稳定的软串口,现有的软串口程序是通过中断实现的,但中断好像会被其他中断打断,导致数据丢失,定时器按文档上的说法,只能大于50us,能不能实现时间小于3us的
    发表于 07-19 06:13

    智能插座“云”时代:定时任务与事件驱动的创新管理

    用户可以通过云端界面,在任何时间任何地点对插座进行配置和监控,同时收集数据和洞察分析,以促进能效最优化。无论是确保家中的咖啡机在你醒来之前准备好早晨的咖啡,还是远程调整办公室的温度设置以节约能源,智能插座配合云管理打开了便捷与高效的大门。
    的头像 发表于 07-15 18:16 1138次阅读
    智能插座“云”时代:<b class='flag-5'>定时任务</b>与事件驱动的创新管理

    定时器的工作方式介绍

    实现周期性事件的硬件模块。它可以用于实现各种定时任务,如定时中断、PWM(脉冲宽度调制)输出、频率测量等。定时器通常由一个计数器、一个时钟
    的头像 发表于 07-12 10:29 1261次阅读

    长持续时间定时器电路图 时间定时器的工作原理和功能

    的处理,都离不开定时器的精确控制。时间定时器通常由硬件和软件两部分组成,硬件部分通过计时器芯片或计数器来实现时间的度量和计算,而软件部分则是通过编程语言提供的函数或类库来设置和处理定时任务
    的头像 发表于 06-24 17:34 2811次阅读
    长持续时间<b class='flag-5'>定时</b>器电路图 时间<b class='flag-5'>定时</b>器的工作原理和功能

    PLC工程示例之步进电机

    电子发烧友网站提供《PLC工程示例之步进电机.rar》资料免费下载
    发表于 06-11 09:09 8次下载

    为了低功耗每2秒将STM32F4的ADC做一次standby可不可行?

    问题来了,1)这样的是否频繁了? 2)这样的ADC稳定需要多长时间?可不可操作? 3)有没有其它模式比standby模式更有利?比如stop等。 功耗要求:150000mah的电池供电达到5年以上。
    发表于 05-16 07:48

    freertos如何周期性的执行一个任务,比如200ms调用一次任务, 用定时器发消息?

    freertos如何周期性的执行一个任务,比如200ms调用一次任务, 用定时器发消息?有没有一个函数能直接实现
    发表于 05-07 08:21

    在物通博联工业智能网关的本地配置界面(WEB)直接配置定时控制任务

    开关,可实现全年定时任务自动执行。在多个任务日期重叠时,可选执行高等级的还是并行执行。设有一个远程和本地的控制点,默认为本地状态,网关自己执行设置的任务,当用户将改控制点切换为远程时可
    的头像 发表于 04-24 17:21 655次阅读
    在物通博联工业智能网关的本地配置界面(WEB)直接配置<b class='flag-5'>定时</b>控制<b class='flag-5'>任务</b>