使用@Scheduled和@EnableScheduling但出现了NoSuchBeanDefinitionException异常

32

我已经按照网上的非常简单的示例在Spring中设置了一个cron任务,但每次启动Tomcat时都会在日志中看到以下错误信息:

2015-05-25 00:32:58 DEBUG ScheduledAnnotationBeanPostProcessor:191 - 
Could not find default TaskScheduler bean org.springframework.beans.factory.NoSuchBeanDefinitionException: No 
qualifying bean of type [org.springframework.scheduling.TaskScheduler] is defined

2015-05-25 00:32:58 DEBUG ScheduledAnnotationBeanPostProcessor:202 - Could not    
find default ScheduledExecutorService bean
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying    
bean of type [org.springframework.scheduling.TaskScheduler] is defined

用于实现cron的2个Java类:

  • @Configuration类:

@Configuration
@EnableScheduling
public class ClearTokenStoreCronEnable {    
  final static Logger log =   
  LoggerFactory.getLogger(ClearTokenStoreCronEnable.class);
  private @Autowired TokenStoreRepository tokenStoreRepository; 
}
  • 以及 Cron 作业类:

    @Service
    public class ClearTokenStoreWorkerService {
    
        final static Logger log = LoggerFactory.getLogger(ClearTokenStoreWorkerService.class);
        private @Autowired TokenStoreRepository tokenStoreRepository;
    
        //@Scheduled(fixedDelay=5000)
        //run daily at midnight
        @Scheduled(cron = "0 0 * * * *")
        public void tokenStoreTable() {
            log.debug("tokenstore table truncated - start");
            tokenStoreRepository.deleteAll();
            log.debug("tokenstore table truncated - end");
        }
    }
    
  • 顺便说一下,定时任务是在午夜运行的,但它似乎也会在其他时间随机运行。不确定这是否是一个 bug 或者我的 cron 表达式有误:@Scheduled(cron = "0 0 * * * *")

    我现在主要关心的是为什么会出现 ScheduledAnnotationBeanPostProcessor 错误?它正在寻找 TaskScheduler 和 ScheduledExectorService。 我只需要每天执行一次。我没有进行并发处理或需要多个线程的操作。这些错误最终是有害的还是需要修复它们呢?


    1
    我发现我最初使用的cron表达式是不正确的。每天午夜触发一次的正确表达式是:@Scheduled(cron = "0 0 0 * * ?")。 - logixplayer
    1
    这不是一个错误。 - narendra-choudhary
    5个回答

    26

    根据异常信息Could not find default TaskScheduler bean,配置应该定义TaskScheduler而不是Executor

    @Configuration
    public class AppContext extends WebMvcConfigurationSupport {
        @Bean
        public TaskScheduler taskScheduler() {
            return new ConcurrentTaskScheduler();
        }
    
        // Of course , you can define the Executor too
        @Bean
        public Executor taskExecutor() {
            return new SimpleAsyncTaskExecutor();
       }
    }
    

    19

    编辑:最佳答案在这里,它涉及创建一个 Executor:

    @Configuration
    @EnableAsync
    public class AppContext extends WebMvcConfigurationSupport {
        @Bean
        public Executor taskExecutor() {
            return new SimpleAsyncTaskExecutor();
        }
    }
    

    之前版本(仍然有效):

    NoSuchBeanDefinitionException会以DEBUG级别记录,可以安全忽略。如果您查看ScheduledAnnotationBeanPostProcessor的源代码,您会发现它首先尝试获取TaskScheduler,然后是ScheduledExecutorService,然后继续“回退到默认调度程序”:

        if (this.registrar.hasTasks() && this.registrar.getScheduler() == null) {
            Assert.state(this.beanFactory != null, "BeanFactory must be set to find scheduler by type");
            try {
                // Search for TaskScheduler bean...
                this.registrar.setScheduler(this.beanFactory.getBean(TaskScheduler.class));
            }
            catch (NoUniqueBeanDefinitionException ex) {
                throw new IllegalStateException("More than one TaskScheduler exists within the context. " +
                        "Remove all but one of the beans; or implement the SchedulingConfigurer interface and call " +
                        "ScheduledTaskRegistrar#setScheduler explicitly within the configureTasks() callback.", ex);
            }
            catch (NoSuchBeanDefinitionException ex) {
                logger.debug("Could not find default TaskScheduler bean", ex);
                // Search for ScheduledExecutorService bean next...
                try {
                    this.registrar.setScheduler(this.beanFactory.getBean(ScheduledExecutorService.class));
                }
                catch (NoUniqueBeanDefinitionException ex2) {
                    throw new IllegalStateException("More than one ScheduledExecutorService exists within the context. " +
                            "Remove all but one of the beans; or implement the SchedulingConfigurer interface and call " +
                            "ScheduledTaskRegistrar#setScheduler explicitly within the configureTasks() callback.", ex);
                }
                catch (NoSuchBeanDefinitionException ex2) {
                    logger.debug("Could not find default ScheduledExecutorService bean", ex);
                    // Giving up -> falling back to default scheduler within the registrar...
                }
            }
        }
    

    您可以通过将org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor的日志级别设置为INFO及以上来删除该异常,例如:

    <logger name="org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor" level="INFO"/>
    

    在使用logback时。

    定时表达式有六个字段:

    second (0-59), minute (0-59), hour (0-23, 0 = midnight), day (1-31), month (1-12), weekday (1-7, 1 = Sunday)
    

    语法可以在quartz文档中找到。 我对“?”字符不太确定,因为尽管页面上写着:

    '?'字符允许出现在月份和星期几字段中。它用于指定“没有具体值”。当您需要在两个字段中指定某些内容,但不需要在另一个字段中指定时,这非常有用。

    但是该页面上的示例即使另一个字段是*也会使用?。 在我看来,只需使用*就可以解决所有问题,因此为了在每天午夜执行,表达式应该是:

    0 0 0 * * *
    

    6
    为了解决这个问题,只需在配置中创建任务调度器bean。
    @Bean
        public TaskScheduler taskScheduler() {
            return new ConcurrentTaskScheduler();
        }
    

    5
    使用Spring Boot 2.0.5,我一直收到以下错误提示:
    2018-11-20 11:35:48.046  INFO 64418 --- [  restartedMain] s.a.ScheduledAnnotationBeanPostProcessor : 
    No TaskScheduler/ScheduledExecutorService bean found for scheduled processing
    

    唯一摆脱它的方法似乎是在您的@Configuration类中使用SchedulingConfigurer接口,如下所示:
    @Configuration
    public class SchedulerConfig implements SchedulingConfigurer {
        private final int POOL_SIZE = 10;
    
        @Override
        public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
            ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
    
            threadPoolTaskScheduler.setPoolSize(POOL_SIZE);
            threadPoolTaskScheduler.setThreadNamePrefix("my-scheduled-task-pool-");
            threadPoolTaskScheduler.initialize();
    
            scheduledTaskRegistrar.setTaskScheduler(threadPoolTaskScheduler);
        }
    }
    

    注意:这是从 https://www.callicoder.com/spring-boot-task-scheduling-with-scheduled-annotation/ 获取的。

    奇怪的是,POOL_SIZE = 1; 似乎不起作用,尽管poolSize可以为1或更高。 - Tito
    2
    值得一提的是:这是一个INFO语句,而不是错误。该语句仅表示无法找到任何自定义调度程序配置(这是可以接受的)。 - narendra-choudhary

    3

    我同意你可以忽略它,但仅仅改变严重性并不能解决问题。我遇到了同样的问题,但我使用的是xml而不是注释,在我的情况下,这是因为我没有在bean定义中包含执行程序。所以添加这个就解决了:

    <task:annotation-driven executor="myExecutor"
        scheduler="myScheduler" />
    <task:executor id="myExecutor" pool-size="5" />
    <task:scheduler id="myScheduler" pool-size="10" />
    

    我希望你能够受益。问候。

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接