How to enable TaskScheduler in spring-boot?

membersound picture membersound · Feb 11, 2015 · Viewed 21.6k times · Source

I'm using spring-boot to set up spring defaults. I'd like to use the @EnableScheduling mechanism, and schedule my tasks conditional.

Therefore I have to implement SchedulingConfigurer and set TaskScheduler explicit.

But when injecting the TaskScheduler, I get the following error. But why doesn't spring-boot automatically provide a Scheduler accordingly?

@Configuration
@EnableAutoConfiguration
@EnableScheduling
public class AppConfig {

}

@Service
public class JobService implements SchedulingConfigurer {
    @Autowired
    private TaskScheduler taskScheduler;

    //schedule the task dynamically
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(taskScheduler); //fails
        taskRegistrar.addTriggerTask(task, trigger);
    }
}

Error:

Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private org.springframework.scheduling.TaskScheduler; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.scheduling.TaskScheduler] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:561)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
    ... 15 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.scheduling.TaskScheduler] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1308)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1054)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:949)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:533)
    ... 17 more

Answer

Andy Wilkinson picture Andy Wilkinson · Feb 11, 2015

You don't need to set a scheduler on the ScheduledTaskRegistrar. If one hasn't been configured it defaults to a ConcurrentTaskScheduler that wraps a single-threaded scheduled executor:

if (this.taskScheduler == null) {
    this.localExecutor = Executors.newSingleThreadScheduledExecutor();
    this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
}

If you're happy with this default scheduler you can remove your autowiring of TaskScheduler and the call to set it on the ScheduledTaskRegistrar. Also, as Marten's suggested in the comments, you should make your SchedulingConfigurer a configuration class rather than a service.

These changes leave your code looking something like this:

@Configuration
static class TaskConfiguration implements SchedulingConfigurer {

    //schedule the task dynamically
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.addTriggerTask(task, trigger);
    }
}