首页>>后端>>SpringBoot->SpringBoot实现固定、动态定时任务的三种实现方式

SpringBoot实现固定、动态定时任务的三种实现方式

时间:2023-11-29 本站 点击:0

背景

最近要用到这个定时任务,之前就简单使用注解的那种方式,需求一变化,就得重新修改。就想到了动态定时任务,连接数据库来动态选择,这样确实解决了问题。但是仍然有一个缺陷,就是没法设置任务的执行时间,无法做到像 QQ 发说说那样,给 xdm 祝福生日时,设定说说为晚上00:00发布。本文就以上三点用自己的思路写了一个小Demo,希望对大家有所帮助。

前言:

阅读完本文:

知晓 SpringBoot 用注解如何实现定时任务

明白 SpringBoot 如何实现一个动态定时任务 (与数据库相关联实现)

理解 SpringBoot 实现设置时间执行定时任务 (使用 ThreadPoolTaskScheduler实现)

一、注解实现定时任务

用注解实现是真的简单,只要会 cron 表达式就行。?‍♂️

第一步: 主启动类上加上 @EnableScheduling 注解

@EnableScheduling@SpringBootApplicationpublicclassSpringBootScheduled{publicstaticvoidmain(String[]args){SpringApplication.run(SpringBootScheduled.class);}}

第二步:写一个类,注入到Spring,关键就是 @Scheduled 注解。 () 里就是 cron 表达式,用来说明这个方法的执行周期的。 ?

我常常也记不住,通常是在线生成的: Cron 表达式在线生成

/***定时任务静态定时任务**第一位,表示秒,取值0-59*第二位,表示分,取值0-59*第三位,表示小时,取值0-23*第四位,日期天/日,取值1-31*第五位,日期月份,取值1-12*第六位,星期,取值1-7,1表示星期天,2表示星期一*第七位,年份,可以留空,取值1970-2099*@authorcrush*@since1.0.0*@Date:2021-07-2721:13*/@ComponentpublicclassSchedulingTaskBasic{/***每五秒执行一次*/@Scheduled(cron="*/5****?")privatevoidprintNowDate(){longnowDateTime=System.currentTimeMillis();System.out.println("固定定时任务执行:--->"+nowDateTime+",此任务为每五秒执行一次");}}

执行效果:

源码在文末。?

二、动态定时任务

其实也非常的简单。

2.1、建数据表

第一步:建个数据库表。

CREATETABLE`tb_cron`(`id`bigint(20)NOTNULLAUTO_INCREMENTCOMMENT'动态定时任务时间表',`cron_expression`varchar(50)CHARACTERSETutf8COLLATEutf8_general_ciNOTNULLCOMMENT'定时任务表达式',`cron_describe`varchar(50)CHARACTERSETutf8COLLATEutf8_general_ciNULLDEFAULTNULLCOMMENT'描述',PRIMARYKEY(`id`)USINGBTREE)ENGINE=InnoDBAUTO_INCREMENT=3CHARACTERSET=utf8COLLATE=utf8_general_ciROW_FORMAT=Dynamic;INSERTINTO`tb_cron`VALUES(1,'00/1***?','每分钟执行一次');

2.2、导入依赖,基础编码

第二步:导入数据库相关依赖,做到能从数据库查询数据。大家都会。?‍♂️

第三步: 编码

实体类:

@Data@TableName("tb_cron")publicclassCron{privateLongid;privateStringcronExpression;privateStringcronDescribe;}

mapper层:

@RepositorypublicinterfaceCronMapperextendsBaseMapper<Cron>{@Select("selectcron_expressionfromtb_cronwhereid=1")StringgetCron1();}

2.3、主要实现代码

第四步:写一个类 实现 SchedulingConfigurer?

实现 void configureTasks(ScheduledTaskRegistrar taskRegistrar); 方法,此方法的作用就是根据给定的 ScheduledTaskRegistrar 注册 TaskScheduler 和特定的Task实例

@ComponentpublicclassCompleteScheduleConfigimplementsSchedulingConfigurer{@Autowired@SuppressWarnings("all")CronMappercronMapper;/***执行定时任务.*/@OverridepublicvoidconfigureTasks(ScheduledTaskRegistrartaskRegistrar){taskRegistrar.addTriggerTask(//1.添加任务内容(Runnable)()->System.out.println("执行动态定时任务1:"+LocalDateTime.now().toLocalTime()+",此任务执行周期由数据库中的cron表达式决定"),//2.设置执行周期(Trigger)triggerContext->{//2.1从数据库获取执行周期Stringcron=cronMapper.getCron1();//2.2合法性校验.if(cron!=null){//OmittedCode..}//2.3返回执行周期(Date)returnnewCronTrigger(cron).nextExecutionTime(triggerContext);});}}

2.4、效果

注意:当你修改了任务执行周期后,生效时间为执行完最近一次任务后。这一点是需要注意的,用生活中的例子理解就是我们取消电话卡的套餐也要下个月生效,含义是一样的。

源码同样在文末。

三、实现设置时间定时任务

通常业务场景是我前言中说的那样,是一次性的定时任务。如:我设置了我写的这篇文章的发布时间为今天下午的两点,执行完就删除没有了。一次性的。

实现主要依靠于 TaskSchedulerScheduledFuture<?> schedule(Runnable task, Trigger trigger);方法来实现。其本质和动态定时任务的实现是一样的。

3.1、实现重点

代码中都含有注解,不多做阐述。

importcn.hutool.core.convert.ConverterRegistry;importcom.crush.scheduled.entity.Task;importlombok.extern.slf4j.Slf4j;importorg.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;importorg.springframework.stereotype.Component;importjava.time.LocalDateTime;importjava.util.*;importjava.util.concurrent.ConcurrentHashMap;importjava.util.concurrent.CopyOnWriteArrayList;importjava.util.concurrent.ScheduledFuture;/***@authorcrush*/@Component@Slf4jpublicclassDynamicTaskService{/***以下两个都是线程安全的集合类。*/publicMap<String,ScheduledFuture<?>>taskMap=newConcurrentHashMap<>();publicList<String>taskList=newCopyOnWriteArrayList<String>();privatefinalThreadPoolTaskSchedulersyncScheduler;publicDynamicTaskService(ThreadPoolTaskSchedulersyncScheduler){this.syncScheduler=syncScheduler;}/***查看已开启但还未执行的动态任务*@return*/publicList<String>getTaskList(){returntaskList;}/***添加一个动态任务**@paramtask*@return*/publicbooleanadd(Tasktask){//此处的逻辑是,如果当前已经有这个名字的任务存在,先删除之前的,再添加现在的。(即重复就覆盖)if(null!=taskMap.get(task.getName())){stop(task.getName());}//hutool工具包下的一个转换类型工具类好用的很ConverterRegistryconverterRegistry=ConverterRegistry.getInstance();DatestartTime=converterRegistry.convert(Date.class,task.getStart());//schedule:调度给定的Runnable,在指定的执行时间调用它。//一旦调度程序关闭或返回的ScheduledFuture被取消,执行将结束。//参数://任务–触发器触发时执行的Runnable//startTime–任务所需的执行时间(如果这是过去,则任务将立即执行,即尽快执行)ScheduledFuture<?>schedule=syncScheduler.schedule(getRunnable(task),startTime);taskMap.put(task.getName(),schedule);taskList.add(task.getName());returntrue;}/***运行任务**@paramtask*@return*/publicRunnablegetRunnable(Tasktask){return()->{log.info("---动态定时任务运行---");try{System.out.println("此时时间==>"+LocalDateTime.now());System.out.println("task中设定的时间==>"+task);Thread.sleep(10);}catch(InterruptedExceptione){e.printStackTrace();}log.info("---end--------");};}/***停止任务**@paramname*@return*/publicbooleanstop(Stringname){if(null==taskMap.get(name)){returnfalse;}ScheduledFuture<?>scheduledFuture=taskMap.get(name);scheduledFuture.cancel(true);taskMap.remove(name);taskList.remove(name);returntrue;}}

3.2、异步线程池的配置

/***异步线程池ThreadPoolExecutor配置类**@Author:crush*@Date:2021-07-2314:14*/@ConfigurationpublicclassThreadPoolTaskExecutorConfig{@BeanpublicThreadPoolTaskSchedulersyncScheduler(){ThreadPoolTaskSchedulersyncScheduler=newThreadPoolTaskScheduler();syncScheduler.setPoolSize(5);//这里给线程设置名字,主要是为了在项目能够更快速的定位错误。syncScheduler.setThreadGroupName("syncTg");syncScheduler.setThreadNamePrefix("syncThread-");syncScheduler.initialize();returnsyncScheduler;}}

3.3、业务代码

这里需要注意一个点,我给项目中的 LocalDateTime 做了类型转换。这里没贴出来(主要是复制以前的代码遗留下来的,源码中都有)

大家简单使用,可以直接用注解 标注在 LocalDateTime 属性上即可。

packagecom.crush.scheduled.controller;importcom.crush.scheduled.entity.Task;importcom.crush.scheduled.service.DynamicTaskService;importorg.springframework.web.bind.annotation.*;importjava.util.List;/***@Author:crush*@Date:2021-07-2915:26*version1.0*/@RestController@RequestMapping("/dynamicTask")publicclassDynamicTaskController{privatefinalDynamicTaskServicedynamicTask;publicDynamicTaskController(DynamicTaskServicedynamicTask){this.dynamicTask=dynamicTask;}/***查看已开启但还未执行的动态任务*@return*/@GetMappingpublicList<String>getStartingDynamicTask(){returndynamicTask.getTaskList();}/***开启一个动态任务*@paramtask*@return*/@PostMapping("/dynamic")publicStringstartDynamicTask(@RequestBodyTasktask){//将这个添加到动态定时任务中去dynamicTask.add(task);return"动态任务:"+task.getName()+"已开启";}/***根据名称停止一个动态任务*@paramname*@return*/@DeleteMapping("/{name}")publicStringstopDynamicTask(@PathVariable("name")Stringname){//将这个添加到动态定时任务中去if(!dynamicTask.stop(name)){return"停止失败,任务已在进行中.";}return"任务已停止";}}

简单封装的一个实体类:

/***@Author:crush*@Date:2021-07-2915:35*version1.0*/@Data@Accessors(chain=true)//方便链式编写习惯所然publicclassTask{/***动态任务名曾*/privateStringname;/***设定动态任务开始时间*/privateLocalDateTimestart;}

3.4、效果

??

开启一个动态任务:

查看开启还未执行的动态任务:

执行结果:

和我们代码中是一模一样的。

停止任务:

再去查看就是已经停止的拉

四、自言自语

源码:springboot-scheduled

本文就是简单介绍了,具体使用时还需要根据具体情况具体分析啦。

你好,我是博主宁在春,希望本篇文章能让你感到有所收获!!!


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/SpringBoot/356.html