首页 > 其他分享 >Spring Boot 实现动态修改定时任务的执行时间

Spring Boot 实现动态修改定时任务的执行时间

时间:2024-08-02 17:18:36浏览次数:8  
标签:Spring Boot springframework cron import scheduling org 定时 public

Spring Boot 实现动态修改定时任务的执行时间

前提

大家通过Spring Boot跑定时任务,用的最多的是@Scheduled,通过指定cron来定时的执行任务,cron可以使用SPEL表达式来通过配置文件配置,但是项目一旦启动,执行时间便无法修改,不够方便,本文基于此来优化项目启动之后不能动态修改cron的问题,实现任务执行时间可以通过Apollo等配置中心实时,动态的修改。

方案一

Spring 有提供 SchedulingConfigurer 接口来定制化定时任务,可以在new CronTrigger的时候放入一个动态的配置key来更改执行时间,优点是可以在项目启动后通过Apollo等配置中心动态的修改执行时间,缺点是一个定时任务就要新建一个类实现此接口,不够优雅。具体示例如下:

package com.ming.tiny.scheduling;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class TestSchedulingConfigurer implements SchedulingConfigurer {


	@Value("${testSchedulingConfigurer.cron=0/5 * * * * ?}")
	private String cron;

	private void testSchedulingConfigurer() {
		log.info("testSchedulingConfigurer 。。。");
	}

	@Override
	public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
		taskRegistrar.addTriggerTask(this::testSchedulingConfigurer, triggerContext -> {
			CronTrigger trigger = new CronTrigger(this.cron);
			return trigger.nextExecutionTime(triggerContext);
		});
	}
}

方案二

方案一可以在项目启动之后动态的修改cron,但是不够灵活,方案二在使用@Scheduled的情况下,结合SchedulingConfigurer来实现动态的修改cron,原理是通过ScheduledTaskRegistrar获取到所有的定时任务,判断哪些是动态配置的cron,把它拿出来通过方案一加入定时任务,共两种方式,如下:

  1. 自定义注解DynamicCron,指定某个任务为动态配置cron

    	@DynamicCron(cronKey = "testAnnotation.cron")
    	@Scheduled(cron = "${testAnnotation.cron:0/5 * * * * ?}")
    	public void testAnnotation() {
    		log.info("testAnnotation。。。");
    	}
    
  2. cron 的 key 为方法的全路径且配置文件有配置时,则可以通过Apollo等配置中心动态调整执行时间

    	/**
    	 * cron 的 key 为方法的全路径且配置文件有配置时,则可以通过Apollo等配置中心动态调整执行时间
    	 */
    	@Scheduled(cron = "${com.ming.tiny.scheduling.TestScheduling.test}")
    	public void test() {
    		log.info("testScheduling。。。");
    	}
    

具体实现如下:

package com.ming.tiny.scheduling;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface DynamicCron {

	String cronKey();

}

主要实现逻辑如下:

package com.ming.tiny.scheduling;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.CronTask;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;

@Slf4j
@Component
public class SchedulingConfig implements SchedulingConfigurer {

	@Autowired
	private Environment environment;

	@Override
	public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
		List<CronTask> cronTaskList = new ArrayList<>(taskRegistrar.getCronTaskList());
		Iterator<CronTask> iterator = cronTaskList.iterator();
		while (iterator.hasNext()) {
			CronTask cronTask = iterator.next();
			String fullPath = cronTask.toString();
			log.info("cronTaskList:{}", fullPath);
			DynamicCron dynamicCron = getDynamicCron(fullPath);
			String dynamicCronKey;
			if (Objects.isNull(dynamicCron)) {
				// 如果key为全路径,且配置文件有配置,则加入动态 Cron
				String cronExpression = this.environment.getProperty(cronTask.toString(), "");
				if (StringUtils.isNotBlank(cronExpression)) {
					dynamicCronKey = cronTask.toString();
				} else {
					dynamicCronKey = "";
				}
			} else {
				dynamicCronKey = dynamicCron.cronKey();
			}
			if (StringUtils.isNotBlank(dynamicCronKey)) {
				iterator.remove();
				taskRegistrar.addTriggerTask(cronTask.getRunnable(), triggerContext -> {
					CronTrigger trigger = new CronTrigger(this.environment.getProperty(dynamicCronKey, cronTask.getExpression()));
					return trigger.nextExecutionTime(triggerContext);
				});
				log.info("addTriggerTask:{}", cronTask);
			}
		}
		taskRegistrar.setCronTasksList(cronTaskList);
	}

	private DynamicCron getDynamicCron(String fullPath) {
		try {
			int lastDotIndex = fullPath.lastIndexOf('.');
			// 加载类
			Class<?> clazz = Class.forName(fullPath.substring(0, lastDotIndex));
			// 获取方法
			Method method = clazz.getDeclaredMethod(fullPath.substring(lastDotIndex + 1));
			// 获取注解
			return method.getAnnotation(DynamicCron.class);
		} catch (Exception e) {
			log.error("getAnnotation err", e);
			throw new RuntimeException(e);
		}
	}
}

测试用例如下:

package com.ming.tiny.scheduling;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Slf4j
@EnableScheduling
@Component
public class TestScheduling {

	/**
	 * cron 的 key 为方法的全路径且配置文件有配置时,则可以通过Apollo等配置中心动态调整执行时间
	 */
	@Scheduled(cron = "${com.ming.tiny.scheduling.TestScheduling.test}")
	public void test() {
		log.info("testScheduling。。。");
	}

	/**
	 * 像这种不是全路径的,无法解析,项目启动之后无法动态调整执行时间
	 */
	@Scheduled(cron = "${testNoDynamic.cron:0/5 * * * * ?}")
	public void testNoDynamic() {
		log.info("testNoDynamic。。。");
	}

	@DynamicCron(cronKey = "testAnnotation.cron")
	@Scheduled(cron = "${testAnnotation.cron:0/5 * * * * ?}")
	public void testAnnotation() {
		log.info("testAnnotation。。。");
	}

}

总结

看过网上的一些实现方案,大多都比较复杂,仔细阅读 Spring 相关源码后,站在巨人的肩膀上,充分利用了 Spring 提供的 SchedulingConfigurer 接口,以最简单的方式实现了可通过Apollo等配置中心在项目启动之后动态的调整任务的执行时间,希望大家喜欢。

标签:Spring,Boot,springframework,cron,import,scheduling,org,定时,public
From: https://www.cnblogs.com/x-mming/p/18339186

相关文章

  • SpringCloud入门学习笔记(四)
    Sentinel篇 SpringCloud入门学习笔记(一)-CSDN博客SpringCloud入门学习笔记(二)-CSDN博客SpringCloud入门学习笔记(三)-CSDN博客前言 在互联网应用过程中,有很多的高并发访问场景,类似于双十一这种活动,特点是访问量剧增,访问量超出系统所能处理的最大并发数。 如果没有保护机......
  • SpringCloud入门学习笔记(三)
    Nacos篇SpringCloud入门学习笔记(二)-CSDN博客SpringCloud入门学习笔记(一)-CSDN博客前言  上篇中提到服务消费者要去调用多个服务提供者构成的集群,此时需要一个三方软件来同步更新提供者的地址信息,同时供服务消费者来此处访问地址,为了解决这类问题,就需要引入服务注册组件(功......
  • 微服务集成springsecurity:功能授权与数据授权
    前言:最近怎么学习真没状态~~~~懒狗模式启动,听说好多在校考研的同学最近也学不下去了。哎呀,最近面了一下鲸浩科技的实习,到现在已经一天没回了,虽然我觉得面得还不错不过应该是挂了吧。功能授权:基于注解preAuthorize,实现RBAC模型提供的权限授权数据授权:这个就比较简单了,通俗的讲就......
  • springboot+vue前后端分离项目-项目搭建15-集成JWT token权限验证
    1.对之前的代码改造,之前将user存储到sessionStorage,改成存储到localStorage,全局搜索修改 之前Result.code等于0代表success,改成200代表success,vue文件全局搜索修改一、前端部分1.改造request.js,登录时将user已经存储到localStorage里,这里将user获取到,将user里的token放到......
  • SpringCloud EasyConfig介绍与使用
    maven引入方式<dependency><groupId>icu.liufuqiang</groupId><artifactId>spring-cloud-easy-config-starer</artifactId><version>0.1.0</version></dependency>仓库地址https://gitee.com/LiuFqiang/spring-c......
  • SpringCloud使用Sentinel,Sentinel持久化,Sentinel使用nacos持久化
    Sentinel官方文档:https://sentinelguard.io/zh-cn/docs/introduction.html下载Sentinel:https://github.com/alibaba/Sentinel/releasessentinel控制台文档:https://sentinelguard.io/zh-cn/docs/dashboard.html参考:https://www.cnblogs.com/ralgo/p/14152390.html启动Sentinel命令:j......
  • 基于SpringBoot的智能购房推荐系统-09040(免费领源码)可做计算机毕业设计JAVA、PHP、爬
    Springboot智能购房推荐系统摘 要近年来随着我国经济的高速发展,房地产业也随之蓬勃发展,尤其是最近国家新出台的房改政策。鼓励居民购房,这对房产公司无疑是一个极好的发展势头。尤为重要的是,近几年随着信息技术和电子商务的快速发展,许多企业都开发了自己房产信息软件。智......
  • SpringBoot入门、进阶、强化、扩展、知识体系完善等知识点学习、性能优化、源码分析专
    场景作为一名Java开发者,SpringBoot已经成为日常开发所必须。势必经历过从入门到自学、从基础到进阶、从学习到强化的过程。当经历过几年企业级开发的磨炼,再回头看之前的开发过程、成长阶段发现确实是走了好多的弯路。作为一名终身学习的信奉者,秉承Java体系需持续学习、持续优......
  • Springboot Docker Redis Mysql集成
    尽管网上关于SpringbootDockerRedisMysql集成的文档很多,但是很多都是老文档,问题不少,所以我专门整理了这份文档。我家里的笔记本是mac,所以我就在mac上详细说明下我的搭建过程。首先我们需要安装docker,mac上本来就有docker的安装包,因此对于mac来说,安装docker就是一件比较轻松的......
  • 基于springboot+vue.js+uniapp在线考试系统的附带文章源码部署视频讲解等
    在这里插入图片描述@toc前言......