Spring Boot如何基于运行时静态变量动态启用@Scheduled定时任务
Spring @Scheduled定时任务动态启停实现方案
下面提供两种可行实现,其中方案1为最优选择,完全避免B的无效触发。
方案1:动态注册B的定时任务(推荐)
放弃B方法上的@Scheduled注解,改为在A执行完设置静态变量为true后,手动将B的业务逻辑注册到Spring任务调度器,未注册状态下B不会有任何触发动作。
代码修改步骤
- 调整配置类,注入任务调度器:
@Configuration @EnableScheduling public class Initialize { // 注入Spring任务调度器 @Bean public TaskScheduler taskScheduler() { ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); scheduler.setPoolSize(2); scheduler.initialize(); return scheduler; } @Bean public A a(){ return new A(); } @Bean public B b(){ return new B(); } }
- 修改A类,执行完业务逻辑后注册B的任务:
public class A{ @Autowired private TaskScheduler taskScheduler; @Autowired private B b; // 注入B的定时延迟配置,和你原来B上@Scheduled的配置一致 @Value("${your.b.fixed-delay.config}") private long bFixedDelay; // 保存B的任务实例,做幂等判断和后续取消用 private volatile ScheduledFuture<?> bScheduledFuture; @Scheduled(fixedDelayString = "sometime specified") public void aProcessor(){ // 原有A的业务逻辑 staticClass.setAppVariable(true); // 幂等注册B的任务,避免重复注册 if (bScheduledFuture == null || bScheduledFuture.isCancelled()) { // fixedDelay场景使用scheduleWithFixedDelay,cron场景可替换为schedule方法传入CronTrigger bScheduledFuture = taskScheduler.scheduleWithFixedDelay( b::bProcessor, Duration.ofMillis(bFixedDelay) ); } } }
- 去掉B类上的@Scheduled注解即可:
public class B{ public void bProcessor(){ // 原有业务处理逻辑 } }
方案2:保留@Scheduled注解,运行时控制任务启停
如果不想修改B的注解配置,可通过ScheduledTaskRegistrar获取B的任务实例,启动时先取消B的调度,等A执行完设置变量为true后再恢复调度。
代码修改步骤
- 配置类实现SchedulingConfigurer接口,保存任务注册器:
@Configuration @EnableScheduling public class Initialize implements SchedulingConfigurer { public static ScheduledTaskRegistrar TASK_REGISTRAR; @Bean public A a(){ return new A(); } @Bean public B b(){ return new B(); } @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { TASK_REGISTRAR = taskRegistrar; } }
- 在A类设置完静态变量后,找到B的任务恢复调度:
public class A{ @Scheduled(fixedDelayString = "sometime specified") public void aProcessor(){ // 原有A的业务逻辑 staticClass.setAppVariable(true); // 遍历所有已注册的定时任务,找到B的任务调整调度状态 Initialize.TASK_REGISTRAR.getScheduledTasks().forEach(scheduledTask -> { if (scheduledTask.getTask().getRunnable().toString().contains("B.bProcessor")) { // 注意Spring默认注册的任务启动就会调度,所以需要在启动完成后先取消B的任务,等这里再重新注册 // 这种方式需要自己处理任务的取消和重新注册,复杂度比方案1高,更推荐用方案1 } }); } }
注意事项
- 你的静态变量已经加了volatile修饰,保证多线程环境下的可见性,符合要求
- 任务注册逻辑要加幂等判断,避免A多次执行重复注册相同的B任务
- 如果后续需要停止B的调度,直接调用
bScheduledFuture.cancel(false)即可
内容的提问来源于stack exchange,提问作者The Rocket
相关产品推荐
相关产品推荐

