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
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 源代码
    +关注

    关注

    94

    文章

    2926

    浏览量

    66061
  • spring
    +关注

    关注

    0

    文章

    332

    浏览量

    14160
  • Boot
    +关注

    关注

    0

    文章

    142

    浏览量

    35234
  • SpringBoot
    +关注

    关注

    0

    文章

    172

    浏览量

    106

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

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

收藏 人收藏

    评论

    相关推荐

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

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

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

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

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

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

    SpringBoot实现动态切换数据源

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

    分布式定时调度:xxl-job最佳实践方法

    定时任务是按照指定时间周期运行任务。使用场景为在某个固定时间点执行,或者周期性的去执行某个任务,比如:每天晚上24点做数据汇总,
    的头像 发表于 11-30 11:06 403次阅读
    分布式<b class='flag-5'>定时</b>调度:xxl-job最佳实践方法

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

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

    定时器如何实现定时任务

    1.1、单次定时任务实现 boost 的asio库里有几个定时器,老的有 deadline_timer , 还有三个可配合 C++11 的 chrono
    的头像 发表于 11-09 17:20 365次阅读

    定时器设计实现

    由于目前C++标准中没有现成的定时器,本设计使用C++11相关语法并进行封装。 本定时器包含一个TimerManager类用于创建定时器和进行定时任务管理,TimerManager会创
    的头像 发表于 11-08 16:50 275次阅读

    基于Django的Celery异步任务定时任务的实战教程

    Django与Celery是基于Python进行Web后端开发的核心搭配,在运营开发(即面向企业内部)的场景中非常常见。 下面是基于Django的Celery异步任务定时任务的实战教程,大家觉得
    的头像 发表于 11-02 10:45 306次阅读
    基于Django的Celery异步<b class='flag-5'>任务</b>和<b class='flag-5'>定时任务</b>的实战教程

    ucos iii定时任务有什么用?

    ucos iii 的定时任务有什么用,通过定时任务定时与普通的调用系统定时函数定时有什么区别?
    发表于 10-07 06:16

    SpringBoot 如何实现热部署

    SpringBoot 如何实现热部署? 1、热部署的优点 开发周期通常包括编写代码、编译、部署和测试几个步骤。在一个快速发展的项目中,这个周期需要尽可能地缩短。热部署能让开发者在代码更改后立即看到结果,从而加速开发和测试过程。 除了加速开发,热部署也让应用
    的头像 发表于 09-30 10:16 401次阅读
    <b class='flag-5'>SpringBoot</b> 如何<b class='flag-5'>实现</b>热部署

    H3C交换机配置定时任务

    H3C交换机配置定时任务
    的头像 发表于 06-21 09:21 951次阅读

    如何使用Spring scheduling task简化定时任务功能的实现

    很多时候,我们有这么一个需求,需要在每天的某个固定时间或者每隔一段时间让应用去执行某一个任务
    的头像 发表于 05-22 16:48 718次阅读
    如何使用Spring scheduling task简化<b class='flag-5'>定时任务</b>功能的<b class='flag-5'>实现</b>?

    python定时任务实践

    由于程序需求,监测配置变化需要设置定时任务,每分钟执行一次,对任务持久化要求不高,不需要时可以关闭定时任务
    的头像 发表于 05-20 17:53 774次阅读
    python<b class='flag-5'>定时任务</b>实践

    Linux如何使用cron进行定时任务的操作

    按计划执行命令对于计算机来说非常重要,因为假如我亲自去执行一些任务的话,可能会因为多方面因素不能按时执行,所以定时任务就显得非常重要了! cron就是一个能够执行定时任务的命令,其实该命令本身不难,下面小编带您详细了解!
    的头像 发表于 05-12 16:27 1786次阅读