常州网站建设价位,个性化企业网站制作公司,云巅seo,建立个人网站前段时间编写了一篇博客SpringBoot 动态操作定时任务#xff08;启动、停止、修改执行周期#xff0c;该篇博客还是帮助了很多同学。 但是该篇博客中的方法有些不足的地方#xff1a; 只能通过前端控制器controller手动注册任务。【具体的应该是我们提前配置好我们的任务启动、停止、修改执行周期该篇博客还是帮助了很多同学。 但是该篇博客中的方法有些不足的地方 只能通过前端控制器controller手动注册任务。【具体的应该是我们提前配置好我们的任务配置完成后让springboot应用帮我们加载并注册任务】无法打印任务的启动时间、下次执行时间及任务耗时等情况。 所以针对以上的不足我对该方案进行了整改。
新方案涉及4个类 TaskSchedulerConfig 任务调度器及任务注册器的配置ScheduledTaskRegistrar 任务注册器用于将定时任务注册到调度器中ScheduledTaskHolder 任务的包装类ITask 任务抽象类 提醒该定时任务只能实现单机环境下使用无法在分布式环境下使用。
一、核心实现如下
1、配置类TaskSchedulerConfig 该配置类往spring容器中注册了两个bean第一个bean为org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler该bean为spring提供的基于线程池的任务调度器其本质是持有一个java.util.concurrent.ScheduledThreadPoolExecutor这里需要注意初始化ScheduledThreadPoolExecutor的时候最大线程数为Integer.MAX_VALU。 setRemoveOnCancelPolicy方法如果设置为true则在中断当前执行的任务后会将其从任务等待队列如果队列中有该任务中移除。 第二个bean为ScheduledTaskRegistrar即我们的任务注册器该bean需要持有一个任务调度器并且需要配置任务列表【目前任务列表需要手动配置如果同学们想增强的话可以自己实现注解扫描配置或者包扫描配置】。 package com.bbs.config.scheduled;import com.bbs.task.MoniterTask;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;import java.util.Arrays;/*** Author whh* Date: 2023/03/15/ 20:15* description*/
Configuration
public class TaskSchedulerConfig {/**** 本质是ScheduledThreadPoolExecutor的包装其中初始化ScheduledThreadPoolExecutor的构造函数如下* 特别的要注意最大线程数为 Integer.MAX_VALUE** public ScheduledThreadPoolExecutor(int corePoolSize,* ThreadFactory threadFactory,* RejectedExecutionHandler handler) {* super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,* new DelayedWorkQueue(), threadFactory, handler);* }* return*/Beanpublic ThreadPoolTaskScheduler threadPoolTaskScheduler(){ThreadPoolTaskScheduler threadPoolTaskScheduler new ThreadPoolTaskScheduler();threadPoolTaskScheduler.setPoolSize(3);threadPoolTaskScheduler.setRemoveOnCancelPolicy(true);return threadPoolTaskScheduler;}Beanpublic ScheduledTaskRegistrar taskRegistrar(ThreadPoolTaskScheduler scheduler){ScheduledTaskRegistrar taskRegistrar new ScheduledTaskRegistrar(scheduler);MoniterTask moniterTask new MoniterTask(*/30 * * * * ?);moniterTask.setTaskName(监控任务);moniterTask.setTaskDescription(每隔30s对机器进行监控);taskRegistrar.setTaskes(Arrays.asList(moniterTask));return taskRegistrar;}
}
2、任务注册器ScheduledTaskRegistrar 该任务注册器实现了org.springframework.beans.factory.InitializingBean接口所以可以达到配置完成后让springboot应用帮我们加载并注册任务。该bean主要有5个方法包括注册任务、查询所有任务、立即执行任务、暂停任务、任务恢复。其中立即执行任务并未使用调度器的线程执行而是使用用户线程执行。 package com.bbs.config.scheduled;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.util.StringUtils;import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;/*** Author whh* Date: 2023/03/15/ 19:44* description 任务注册*/
public class ScheduledTaskRegistrar implements InitializingBean {private static final Logger LOGGER LoggerFactory.getLogger(ScheduledTaskRegistrar.class);/*** 任务调度器*/private ThreadPoolTaskScheduler scheduler;/*** 任务列表*/private ListITask taskes;private final MapString, ScheduledTaskHolder register new ConcurrentHashMap();public ScheduledTaskRegistrar(ThreadPoolTaskScheduler scheduler) {this.scheduler scheduler;}/*** 注册任务*/public void register() {for (ITask task : taskes) {ScheduledFuture? future this.scheduler.schedule(task, new CronTrigger(task.getCron()));ScheduledTaskHolder holder new ScheduledTaskHolder();holder.setScheduledFuture(future);holder.setTask(task);register.put(task.getClass().getName(), holder);}}/*** 查询所有任务** return*/public CollectionScheduledTaskHolder list() {return register.values();}/*** 立即执行任务** param className*/public void start(String className) {ScheduledTaskHolder holder register.get(className);if (holder ! null) {holder.getTask().run();}}/*** 暂停任务** param className*/public void pause(String className) {ScheduledTaskHolder holder register.get(className);if (holder ! null) {if(holder.terminate()){return;}ScheduledFuture? future holder.getScheduledFuture();future.cancel(true);}}/***重启任务* param className* param cron*/public void restart(String className,String cron){ScheduledTaskHolder holder register.get(className);if (holder ! null) {if(!holder.terminate()){//暂停原任务holder.getScheduledFuture().cancel(true);}ITask task holder.getTask();if(!StringUtils.isEmpty(cron)){task.setCron(cron);}ScheduledFuture? future this.scheduler.schedule(task, new CronTrigger(task.getCron()));holder.setScheduledFuture(future);}}public void setTaskes(ListITask taskes) {this.taskes taskes;}private void log(){register.forEach((k,v)-{LOGGER.info(register {} complete,cron {},k,v.getTask().getCron());});}Overridepublic void afterPropertiesSet(){register();log();}
}
3、任务包装类ScheduledTaskHolder 任务包装类包含具体任务的实例、任务执行的ScheduledFuture以及任务的运行状态。 package com.bbs.config.scheduled;import java.util.concurrent.ScheduledFuture;/*** Author whh* Date: 2023/03/15/ 19:45* description*/
public class ScheduledTaskHolder {/*** 具体任务*/private ITask task;/***result of scheduling*/private ScheduledFuture? scheduledFuture;public ITask getTask() {return task;}public void setTask(ITask task) {this.task task;}public ScheduledFuture? getScheduledFuture() {return scheduledFuture;}public void setScheduledFuture(ScheduledFuture? scheduledFuture) {this.scheduledFuture scheduledFuture;}public boolean terminate() {return scheduledFuture.isCancelled();}
}4、 任务抽象类ITask 任务抽象类ITask实现了Runnable接口同时提供了抽象方法execute用于自定义任务实现同时对任务进行了增强增加了任务执行信息的打印。 package com.bbs.config.scheduled;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.scheduling.support.SimpleTriggerContext;import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Date;/*** Author whh* Date: 2023/03/15/ 19:46* description* 任务的抽象类* 用于做切面打印任务执行的时间耗时等信息*/
public abstract class ITask implements Runnable{private static final Logger LOGGER LoggerFactory.getLogger(ITask);private String cron;private String taskName;private String taskDescription;public ITask(String cron) {this.cron cron;}Overridepublic void run() {LocalDateTime start LocalDateTime.now();execute();LocalDateTime end LocalDateTime.now();Duration duration Duration.between(start, end);long millis duration.toMillis();Date date new CronTrigger(this.cron).nextExecutionTime(new SimpleTriggerContext());LOGGER.info(任务[{}]执行完毕开始时间{}结束时间{}耗时{}ms下次执行时间{},this.taskName, start,end,millis,date);}/*** 用户的任务实现*/public abstract void execute();public String getCron() {return cron;}public void setCron(String cron) {this.cron cron;}public String getTaskName() {return taskName;}public void setTaskName(String taskName) {this.taskName taskName;}public String getTaskDescription() {return taskDescription;}public void setTaskDescription(String taskDescription) {this.taskDescription taskDescription;}
}
二、前端控制器TaskController
package com.bbs.task.controller;import com.bbs.config.scheduled.ITask;
import com.bbs.config.scheduled.ScheduledTaskRegistrar;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.scheduling.support.SimpleTriggerContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;/*** Author whh* Date: 2023/03/15/ 21:18* description*/
RestController
RequestMapping(/task)
public class TaskController {Autowiredprivate ScheduledTaskRegistrar scheduledTaskRegistrar;GetMappingpublic ListMapString,Object listTask(){return scheduledTaskRegistrar.list().stream().map(e-{MapString,Object temp new HashMap();ITask task e.getTask();temp.put(任务名,task.getTaskName());temp.put(任务描述,task.getTaskDescription());temp.put(cron表达式,task.getCron());temp.put(任务状态,e.terminate()?已停止:运行中);temp.put(任务类名,task.getClass().getName());temp.put(下次执行时间,new CronTrigger(task.getCron()).nextExecutionTime(new SimpleTriggerContext()));return temp;}).collect(Collectors.toList());}PostMapping(/pause)public void pause(String className){scheduledTaskRegistrar.pause(className);}PostMapping(/start)public void start(String className){scheduledTaskRegistrar.start(className);}PostMapping(/restart)public void restart(String className,String cron){scheduledTaskRegistrar.restart(className,cron);}
}
三、自定义任务实现
package com.bbs.task;import com.bbs.config.scheduled.ITask;/*** Author whh* Date: 2023/03/15/ 21:16* description*/
public class MonitorTask extends ITask {public MonitorTask(String cron) {super(cron);}Overridepublic void execute() {System.out.println(hello world ....);}
}
四、测试
1、应用启动时控制台会打印我们注册的任务信息 2、任务执行情况 3、前端控制器查询任务 调用暂停API后任务停止执行重启任务后任务又重新开始执行。