Spring Boot懒初始化模式下@Scheduled定时任务不生效解决方案
Spring Boot 2.6.7 + JDK17 环境下@Scheduled定时任务不执行问题
问题现象
基于Spring Boot 2.6.7、JDK 17搭建的应用中,@Scheduled标注的定时任务完全不执行;在启动类main方法中添加context.getBean(NoPrimaryServiceTasks.class);提前获取对应Bean后,定时任务即可正常运行。尝试使用Spring Boot官方提供的org.springframework.boot.autoconfigure.task.ScheduledBeanLazyInitializationExcludeFilter也无法解决问题。
关联代码
定时任务配置类
@Configuration @EnableScheduling public class SchedulingConfigurerConfiguration implements SchedulingConfigurer { @Value("${schedule.pool}") private int DYNO_CORES; @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { var taskScheduler = new ThreadPoolTaskScheduler(); taskScheduler.setPoolSize(DYNO_CORES * 4); taskScheduler.initialize(); taskRegistrar.setTaskScheduler(taskScheduler); } @Bean SimpleAsyncTaskExecutor executor() { var executor = new SimpleAsyncTaskExecutor("bg_tasks_"); executor.setConcurrencyLimit(UNBOUNDED_CONCURRENCY);//these jobs are mainly IO-bound, so let's not set a limit for now return executor; } }
定时任务服务类
@Slf4j @Service public class NoPrimaryServiceTasks { @Scheduled(fixedDelay = 1000, initialDelay = 1000 ) public void checkBacklogSize() { log.info("logzzz"); } }
问题根因
- 在
configureTasks方法中手动实例化的ThreadPoolTaskScheduler没有被注册为Spring容器管理的Bean,手动调用initialize()的时机早于@Scheduled注解所在Bean的后置处理流程,导致ScheduledAnnotationBeanPostProcessor扫描@Scheduled方法时,没有正确关联到调度器。 - Spring Boot 2.6+ 版本默认对无显式依赖引用的Bean采用懒初始化策略,定时任务类没有被其他Bean注入依赖,因此启动阶段不会被实例化,其@Scheduled方法自然不会被注册到任务调度器中。
- 官方
ScheduledBeanLazyInitializationExcludeFilter不生效的原因是自定义SchedulingConfigurer的加载触发了ScheduledTaskRegistrar提前初始化,此时过滤器还未完成对定时任务Bean的排除规则加载。 - 原代码中
UNBOUNDED_CONCURRENCY常量未指定所属类,会直接出现编译错误,该常量属于SimpleAsyncTaskExecutor类。
修复方案
- 将
ThreadPoolTaskScheduler声明为Spring容器管理的Bean,由容器负责生命周期管理,不要在configureTasks方法中手动实例化和初始化。 - 给定时任务类添加
@Lazy(false)注解,强制容器在启动阶段立即实例化该类,保证@Scheduled方法能被后置处理器扫描到。 - 修正常量引用的编译错误,给异步执行器Bean指定明确名称,避免和调度器Bean产生冲突。
修复后配置类代码
@Configuration @EnableScheduling public class SchedulingConfigurerConfiguration implements SchedulingConfigurer { @Value("${schedule.pool}") private int dynoCores; @Bean(destroyMethod = "shutdown", name = "taskScheduler") public ThreadPoolTaskScheduler taskScheduler() { ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); taskScheduler.setPoolSize(dynoCores * 4); taskScheduler.setThreadNamePrefix("scheduled-pool-"); taskScheduler.setWaitForTasksToCompleteOnShutdown(true); taskScheduler.setAwaitTerminationSeconds(30); return taskScheduler; } @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { taskRegistrar.setTaskScheduler(taskScheduler()); } @Bean(name = "bgTaskExecutor") SimpleAsyncTaskExecutor bgTaskExecutor() { var executor = new SimpleAsyncTaskExecutor("bg_tasks_"); executor.setConcurrencyLimit(SimpleAsyncTaskExecutor.UNBOUNDED_CONCURRENCY); return executor; } }
修复后定时任务类代码
@Slf4j @Service @Lazy(false) public class NoPrimaryServiceTasks { @Scheduled(fixedDelay = 1000, initialDelay = 1000 ) public void checkBacklogSize() { log.info("logzzz"); } }
如果项目全局开启了懒加载(配置了spring.main.lazy-initialization=true),可以额外在配置类上添加@Import(NoPrimaryServiceTasks.class),显式导入定时任务类,保证其被容器提前扫描加载。
内容的提问来源于stack exchange,提问作者user3756506
相关产品推荐
相关产品推荐

