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

    文章

    2944

    浏览量

    66657
  • spring
    +关注

    关注

    0

    文章

    338

    浏览量

    14307
  • Boot
    +关注

    关注

    0

    文章

    149

    浏览量

    35778
  • SpringBoot
    +关注

    关注

    0

    文章

    173

    浏览量

    167

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

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

收藏 人收藏

    评论

    相关推荐

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

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

    变频器外接按钮如何接线

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

    linux定时任务的用法总结

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

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

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

    PLC工程示例之步进电机

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

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

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

    使用TC21x的GPT实现1m计时器执行定时任务,怎么配置GTM和GPT?

    专家们好,我想使用TC21x的GPT实现1m计时器执行定时任务,不知道怎么配置GTM和GPT?
    发表于 02-06 06:47

    鸿蒙ArkUI开发-实现增删Tab页签

    本文以浏览器中增加或删除页签为例,实现Tabs中页签的增删功能。
    的头像 发表于 01-29 18:43 1538次阅读
    鸿蒙ArkUI开发-<b class='flag-5'>实现</b><b class='flag-5'>增删</b>Tab页签

    鸿蒙原生应用/元服务开发-长时任务

    概述 功能介绍 应用退至后台后,对于在后台需要长时间运行用户可感知的任务,例如播放音乐、导航等。为防止应用进程被挂起,导致对应功能异常,可以申请长时任务,使应用在后台长时间运行。申请长时任务后,系统
    发表于 01-09 10:52

    鸿蒙原生应用/元服务开发-短时任务

    \'@ohos.resourceschedule.backgroundTaskManager\'; 2.申请短时任务实现回调 et id; // 申请短时任务ID let delayTime; // 本次申请短
    发表于 12-28 16:13

    物通博联工业智能网关实现PLC定时控制任务

    。 物通博联工业智能网关不仅支持本地部署,同时也支持远程部署,实现PLC程序的远程读写。企业可通过添加定时控制任务,定义任务策略,设置设备定时
    的头像 发表于 12-27 15:52 310次阅读
    物通博联工业智能网关<b class='flag-5'>实现</b>PLC<b class='flag-5'>定时</b>控制<b class='flag-5'>任务</b>

    多种一键的程序编写方式你会几种?

    对于刚入门的PLC新手来说,在没有理解PLC CPU的扫描工作原理时对于行内偶尔提到的一键程序编写总会有一定的难度!
    的头像 发表于 12-27 11:32 689次阅读
    多种一键<b class='flag-5'>启</b><b class='flag-5'>停</b>的程序编写方式你会几种?

    电机每分钟和正反转10次左右,应该算什么工作制呢?

    电机每分钟和正反转10次左右,应该算什么工作制?
    发表于 12-11 07:39

    SpringBoot实现动态切换数据源

    最近在做业务需求时,需要从不同的数据库中获取数据然后写入到当前数据库中,因此涉及到切换数据源问题。本来想着使用Mybatis-plus中提供的动态数据源SpringBoot的starter:dynamic-datasource-spring-boot-starter来
    的头像 发表于 12-08 10:53 976次阅读
    <b class='flag-5'>SpringBoot</b><b class='flag-5'>实现</b><b class='flag-5'>动态</b>切换数据源

    HarmonyOS后台任务管理开发指南上线!

    时的操作步骤。 ①了解相关机制及规格,实现更高效开发。 ○ 申请时机:应用需要在前台或退至后台 5 秒内申请短时任务。 ○ 数量限制:一个应用同一时刻最多支持申请 3 个。 ○ 配额机制:一个应用有一定时
    发表于 11-29 09:58