Spring Boot如何实现定时任务的动态增删启停详解

网友投稿 224 2023-04-01

Spring Boot如何实现定时任务的动态增删启停详解

我以为动态停启定时任务一般用quartz,没想到还可以通过ScheduledTaskRegistrar来拓展。但是分布式场景,建议还是用quartz吧!

在 spring boot 项目中,可以通过 @EnableScheduling 注解和 @Scheduled 注解实现定时任务,也可以通过 SchedulingConfigurer 接口来实现定时任务。但是这两种方式不能动态添加、删除、启动、停止任务。要实现动态增删启停定时任务功能,比较广泛的做法是集成 Quartz 框架。

但是本人的开发原则是:在满足项目需求的情况下,尽量少的依赖其它框架,避免项目过于臃肿和复杂。查看 spring-context 这个 jar 包中 org.springframework.scheduling.ScheduledTaskRegistrar 这个类的源代码,发现可以通过改造这个类就能实现动态增删启停定时任务功能。

定时任务列表页

定时任务执行日志

添加执行定时任务的线程池配置类

@Configuration

public class SchedulingConfig {

@Bean

public TaskScheduler taskScheduler() {

ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();

taskScheduler.setPoolSize(4);

taskScheduler.setRemoveOnCancelPolicy(true);

taskScheduler.setThreadNamePrefix("TaskSchedulerThreadPool-");

return taskScheduler;

}

}

添加 ScheduledFuture 的包装类。ScheduledFuture 是 ScheduledExecutorService 定时任务线程池的执行结果。

public final class ScheduledTask {

volatile ScheduledFuture> future;

public void cancel() {

ScheduledFuture> future = this.future;

if (future != null) {

future.cancel(true);

}

}

}

添加 Runnable 接口实现类,被定时任务线程池调用,用来执行指定 bean 里面的方法。

public class SchedulingRunnable implements Runnable {

private static final Logger logger = LoggerFactory.getLogger(SchedulingRunnable.class);

private String beanName;

private String methodName;

private String params;

public SchedulingRunnable(String beanName, String methodName) {

this(beanName, methodName, null);

}

public SchedulingRunnable(String beanName, String methodName, String params) {

this.beanName = beanName;

this.methodName = methodName;

this.params = params;

}

@Override

public void run() {

logger.info("定时任务开始执行 - bean:{},方法:{},参数:{}", beanName, methodName, params);

long startTime = System.currentTimeMillis();

try {

Object target = SpringContextUtils.getBean(beanName);

Method method = 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 (Exception ex) {

logger.error(String.format("定时任务执行异常 - bean:%s,方法:%s,参数:%s ", beanName, methodName, params), ex);

}

long times = System.currentTimeMillis() - startTime;

logger.info("定时任务执行结束 - bean:{},方法:{},参数:{},耗时:{} 毫秒", beanName, methodName, params, times);

}

@Override

public boolean equals(Object o) {

if (this == o) return true;

if (o == null || getClass() != o.getClass()) return false;

SchedulingRunnable that = (SchedulingRunnable) o;

if (params == null) {

return beanName.equals(that.beanName) &&

methodName.equals(that.methodName) &&

that.params == null;

}

return beanName.equals(that.beanName) &&

methodName.equals(that.methodName) &&

params.equals(that.params);

}

@Override

public int hashCode() {

if (params == null) {

return Objects.hash(beanName, methodName);

}

return Objects.hash(beanName, methodName, params);

}

}

添加定时任务注册类,用来增加、删除定时任务。

@Component

public class CronTaskRegistrar implements DisposableBean {

private final Map scheduledTasks = new ConcurrentHashMap<>(16);

@Autowired

private TaskScheduler taskScheduler;

public TaskScheduler getScheduler() {

return this.taskScheduler;

}

public void addCronTask(Runnable task, String cronExpression) {

addCronTask(new CronTask(task, cronExpression));

}

public void addCronTask(CronTask cronTask) {

if (cronTask != null) {

Runnable task = cronTask.getRunnable();

if (this.scheduledTasks.containsKey(task)) {

removeCronTask(task);

}

this.scheduledTasks.put(task, scheduleCronTask(cronTask));

}

}

public void removeCronTask(Runnable task) {

ScheduledTask scheduledTask = this.scheduledTasks.remove(task);

if (scheduledTask != null)

scheduledTask.cancel();

}

public ScheduledTask scheduleCronTask(CronTask cronTask) {

ScheduledTask scheduledTask = new ScheduledTask();

scheduledTask.future = this.taskScheduler.schedule(cronTask.getRunnable(), cronTask.getTrigger());

return scheduledTask;

}

@Override

public void destroy() {

for (ScheduledTask task : this.scheduledTasks.values()) {

task.cancel();

}

this.scheduledTasks.clear();

}

}

添加定时任务示例类

@Component("demoTask")

public class DemoTask {

public void taskWithParams(String params) {

System.out.println("执行有参示例任务:" + params);

}

public void taskNoParams() {

System.out.println("执行无参示例任务");

}

}

定时任务数据库表设计

定时任务数据库表设计

添加定时任务实体类

public class SysJobPO {

private Integer jobId;

private String beanName;

private String methodName;

private String methodParams;

private String cronExpression;

private Integer jobStatus;

private String remark;

private Date createTime;

private Date updateTime;

public Integer getJobId() {

return jobId;

}

public void setJobId(Integer jobId) {

this.jobId = jobId;

}

public String getBeanName() {

return beanName;

}

public void setBeanName(String beanName) {

this.beanName = beanName;

}

public String getMethodName() {

return methodName;

}

public void setMethodName(String methodName) {

this.methodName = methodName;

}

public String getMethodParams() {

return methodParams;

}

public void setMethodParams(String methodParams) {

this.methodParams = methodParams;

}

public String getCronExpression() {

return cronExpression;

}

public void setCronExpression(String cronExpression) {

this.cronExpression = cronExpression;

}

public Integer getJobStatus() {

return jobStatus;

}

public void setJobStatus(Integer jobStatus) {

this.jobStatus = jobStatus;

}

public String getRemark() {

return remark;

}

public void setRemark(String remark) {

this.remark = remark;

}

public Date getCreateTime() {

return createTihttp://me;

}

public void setCreateTime(Date createTime) {

this.createTime = createTime;

}

public Date getUpdateTime() {

return updateTime;

}

public void setUpdateTime(Date updateTime) {

this.updateTime = updateTime;

}

}

新增定时任务

新增定时任务

boolean success = sysJobRepository.addSysJob(sysJob);

if (!success)

return OperationResUtils.fail("新增失败");

else {

if (sysJob.getJobStatus().equals(SysJobStatus.NORMAL.ordinal())) {

SchedulingRunnable task = new SchedulingRunnable(sysJob.getBeanName(), sysJob.getMethodName(), sysJob.getMethodParams());

cronTaskRegistrar.addCronTask(task, sysJob.getCronExpression());

}

}

return OperationResUtils.success();

修改定时任务,先移除原来的任务,再启动新任务

boolean success = sysJobRepository.editSysJob(sysJob);

if (!success)

return OperationResUtils.fail("编辑失败");

else {

if (existedSysJob.getJobStatus().equals(SysJobStatus.NORMAL.ordinal())) {

SchedulingRunnable task = new SchedulingRunnable(existedSysJob.getBeanName(), existedSysJob.getMethodName(), existedSysJob.getMethodParams());

cronTaskRegistrar.removeCronTask(task);

}

if (sysJob.getJobStatus().equals(SysJobStatus.NORMAL.ordinal())) {

SchedulingRunnable task = new SchedulingRunnable(sysJob.getBeanName(), sysJob.getMethodName(), sysJob.getMethodParams());

cronTaskRegistrar.addCronTask(task, sysJob.getCronExpression());

}

}

return OperationResUtils.success();

删除定时任务

boolean success = sysJobRepository.deleteSysJobById(req.getJobId());

if (!success)

return OperationResUtils.fail("删除失败");

else{

if (existedSysJob.getJobStatus().equals(SysJobStatus.NORMAL.ordinal())) {

SchedulingRunnable task = new SchedulingRunnable(existedSysJob.getBeanName(), existedSysJob.getMethodName(), existedSysJob.getMethodParams());

cronTaskRegistrar.removeCronTask(task);

}

}

return OperationResUtils.success();

定时任http://务启动 / 停止状态切换

if (existedSysJob.getJobStatus().equals(SysJobStatus.NORMAL.ordinal())) {

SchedulingRunnable task = new SchedulingRunnable(existedSysJob.getBeanName(), existedSysJob.getMethodName(), existedSysJob.gehttp://tMethodParams());

cronTaskRegistrar.addCronTask(task, existedSysJob.getCronExpression());

} else {

SchedulingRunnable task = new SchedulingRunnable(existedSysJob.getBeanName(), existedSysJob.getMethodName(), existedSysJob.getMethodParams());

eDOYWlV cronTaskRegistrar.removeCronTask(task);

}

添加实现了 CommandLineRunner 接口的 SysJobRunner 类,当 spring boot 项目启动完成后,加载数据库里状态为正常的定时任务。

@Service

public class SysJobRunner implements CommandLineRunner {

private static final Logger logger = LoggerFactory.getLogger(SysJobRunner.class);

@Autowired

private ISysJobRepository sysJobRepository;

@Autowired

private CronTaskRegistrar cronTaskRegistrar;

@Override

public void run(String... args) {

List jobList = sysJobRepository.getSysJobListByStatus(SysJobStatus.NORMAL.ordinal());

if (CollectionUtils.isNotEmpty(jobList)) {

for (SysJobPO job : jobList) {

SchedulingRunnable task = new SchedulingRunnable(job.getBeanName(), job.getMethodName(), job.getMethodParams());

cronTaskRegistrar.addCronTask(task, job.getCronExpression());

}

logger.info("定时任务已加载完毕...");

}

}

}

工具类 SpringContextUtils,用来从 spring 容器里获取 bean

@Component

public class SpringContextUtils implements ApplicationContextAware {

private static ApplicationContext applicationContext;

@Override

public void setApplicationContext(ApplicationContext applicationContext)

throws BeansException {

SpringContextUtils.applicationContext = applicationContext;

}

public static Object getBean(String name) {

return applicationContext.getBean(name);

}

public static T getBean(Class requiredType) {

return applicationContext.getBean(requiredType);

}

public static T getBean(String name, Class requiredType) {

return applicationContext.getBean(name, requiredType);

}

public static boolean containsBean(String name) {

return applicationContext.containsBean(name);

}

public static boolean isSingleton(String name) {

return applicationContext.isSingleton(name);

}

public static http://Class extends Object> getType(String name) {

return applicationContext.getType(name);

}

}

总结

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:免费数据库云服务(免费sql云数据库)
下一篇:人像对比api
相关文章

 发表评论

暂时没有评论,来抢沙发吧~