使用Spring中的@Scheduled 配置不同环境下的参数

需求

在项目中直接使用了Spring 提供的 @Scheduled 注解来实现定时任务的配置。然后希望能够根据不同的环境去配置不同的定时任务配置。

实现

先要看这个不同的环境的配置是来自哪里?可以是配置文件,也可以是数据库。

配置来自文件

如果是配置文件就比较简单,可以直接使用 ${ properties:default } 或者 #{ bean.properties?:defualt } 的 SpEL 表达式,来直接获取配置文件中的属性。还可以设置默认值。

配置文件中配置属性

# 在不同的profile文件下去声明配置
scheduled:
  cron:
    task1: 0 0 4 * * ?
    task2: 0 0/1 * * * ?

代码中如何去接收属性

/**
 * 定时任务-直接获取配置属性
 */
@Scheduled(cron = "${scheduled.cron.task1:0 0/1 * * * ?}")
void scheduling() {
    // 定时任务执行的内容
}

---

// 使用一个类,去接受配置文件属性的映射
/**
 * 定时任务的cron表达式配置映射类,可以统一管理默认值。或者配置初始化的代码逻辑
 */
@Configuration
@ConfigurationProperties(prefix = "scheduled.cron")
public class CronConfig {
	private String task1 = "这里可以设置属性的默认值";
	// ...
}
/**
 * 定时任务-间接获取配置属性
 * - cronConfig 一定是要在容器中的名字
 * - getTask1() 方法一定是public的!
 */
@Scheduled(cron = "#{cronConfig.getTask1()}")
void scheduling() {
    // 定时任务执行的内容
}

这样在不同环境下,只需去 application-profile.yml 修改或创建想要设置的属性即可。然后运行时,执行想要加载的环境配置即可。

配置来自数据库(或其它)

这种情况,一般我们只能在项目启动时,与数据库建立连接后去拉取数据库中的配置信息,然后通过编程的方式去动态的创建调度任务。

首先要想通过变成的方式,去动态的设置调度任务的配置。那么我们必须先实现它暴露出来的一个接口 SchedulingConfigurer 它只有一个方法 void configureTasks(ScheduledTaskRegistrar taskRegistrar) 它将调度任务的注册器给暴露出来。这样我们可以通过注册器,去注册我们想要注册的任务(从某种程度上来说,也是替代了注解方式)。

这里先看核心的配置类 SchedulingTaskConfigure

/**
 * 调度任务的自定义配置类
 */
@Configuration
class SchedulingTaskConfigure implements SchedulingConfigurer, SchedulingTaskRegistrar {

    // 存储需要注册的任务列表。当 ScheduledTaskRegistrar 还没有加载进来时,需要注册的任务先暂时放入这里。
    private final List<SchedulingTask> schedulingTaskList;
	// 将注册器保留下来
    private volatile ScheduledTaskRegistrar taskRegistrar;

    public SchedulingTaskConfigure() {
        // 初始化任务列表
        this.schedulingTaskList = new ArrayList<>();
    }

	// 当项目启动后,开启`@EnableScheduling` 注解。然后Spring扫描到 SchedulingConfigurer 有实现类时就会调用这里
	// 这个方法只会调用一次
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        System.out.println("configureTasks is init!");
        this.taskRegistrar = taskRegistrar;

        // fixme 可以支持设置线程池
        taskRegistrar.setScheduler(taskScheduler());
        System.out.println("configureTasks is init! = " + this.schedulingTaskList);

		// 这里是循环去任务列表注册任务
        for (SchedulingTask schedulingTask : schedulingTaskList) {
            taskRegistrar.addTriggerTask(
                    // 执行定时任务
                    () -> {
                        System.out.println("configureTasks is running! 执行定时任务");
                        schedulingTask.taskService();
                    },
                    // 设置触发器
                    triggerContext -> {
                        System.out.println("configureTasks is running! 设置触发器");
                        // 获取定时任务周期表达式
                        final String cron = schedulingTask.cron();
                        CronTrigger trigger = new CronTrigger(cron);
                        return trigger.nextExecutionTime(triggerContext);
                    }
            );
        }
    }

	// 这里其它代码,如果需要定时任务的支持时,就调用这个方法并传递任务的实例
    @Override
    public void add(SchedulingTask schedulingTask) {
        System.out.println("this.schedulingTaskList = " + this.schedulingTaskList + ", taskRegistrar = " + this.taskRegistrar);
        
        if (this.taskRegistrar != null) {
            this.taskRegistrar.addCronTask(schedulingTask::taskService, schedulingTask.cron());
            return;
        }

        this.schedulingTaskList.add(schedulingTask);
    }

    // 这里支持原生线程池,也支持重新@Bean-spring-schedule的线程池
    private Executor taskScheduler() {
        // 设置线程名称。这里使用了guava的库(非必需)
        final ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("demo-pool-%d").build();
        // 创建线程池
        return Executors.newScheduledThreadPool(5, namedThreadFactory);
    }

}

上面的就是我们自定义的调度配置类。其中我们还额外需要两个类 SchedulingTaskRegistrarSchedulingTask。前者是对外提供注册定时任务的接口。后者则是任务的实例类。

/**
 * 对外暴露的定时任务注册接口
 */
public interface SchedulingTaskRegistrar {
	
	// 注册定时任务
    void add(SchedulingTask schedulingTask);

}

/**
 * 声明任务的实例类
 */
public interface SchedulingTask {

    /**
     * 执行定时任务
     */
    void taskService();

    /**
     * cron表达式
     */
    String cron();

}

上述的三个类,就可以帮助我们将数据库中的配置的数据,转为对应的定时任务。在需要的地方去注入 SchedulingTaskRegistrar 即可,然后调用add方法。

PS:这种方式甚至可以针对一些管理后台的项目。比如运营人员动态设置定时任务,检查定时任务的状态,定时任务的开启/关闭等等,都可以通过自定义调度的配置来实现。

参考资料

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring的@Scheduled是一个注解,用于在Spring应用程序实现任务调度和异步方法执行。它提供了对基于cron表达式的任务调度的支持,并可以与触发器元数据一起添加到方法使用@Scheduled注解,可以指定方法在何时执行以及执行的频率。\[1\] 要在Spring应用程序使用@Scheduled注解,需要在应用程序的配置文件添加相应的命名空间和模式。例如,在applicationConfig.xml文件,可以添加以下配置来引入task命名空间和模式:xmlns:task="http://www.springframework.org/schema/task" http://www.springframework.org/schema/task/ http://www.springframework.org/schema/task/spring-task-3.0.xsd\[2\] 使用@Scheduled注解的方法应该没有任何参数,并且不应该返回任何值。如果需要在@Scheduled方法使用外部对象,应该使用自动装配将它们注入到相应的类,而不是将它们作为参数传递给@Scheduled方法。\[3\] 总结起来,@Scheduled是Spring框架用于任务调度和异步方法执行的注解,它可以与cron表达式一起使用,并提供了灵活的任务调度功能。 #### 引用[.reference_title] - *1* *2* *3* [Spring @Scheduled 使用详解](https://blog.csdn.net/DynmicResource/article/details/124013028)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值