-
Notifications
You must be signed in to change notification settings - Fork 41.4k
Description
Both TaskExecutionAutoConfiguration
and TaskSchedulingAutoConfiguration
create Executor
implementations. The first creates ThreadPoolTaskExecutor
while the second creates ThreadPoolTaskScheduler
. Both extend ExecutorConfigurationSupport
and implement AsyncListenableTaskExecutor
and SchedulingTaskExecutor
. The later however also implements TaskScheduler
which is needed when you add @EnableScheduling
to your application.
Tickets #15983 and #15984 made the TaskSchedulingAutoConfiguration
run after TaskExecutionAutoconfiguration
which was, in my opinion bad decisions. Since @EnableScheduling
requires TaskScheduler
then (unless you have other implementation) ThreadPoolTaskScheduler
will be created which results in duplicate Executor
bean (actually duplicate of all interfaces provided by ThreadPoolTaskExecutor
). If your application is using @Autowire
Executor
along with @EnableScheduling
you will get following error:
Field executor in XXX required a single bean, but 2 were found:
- applicationTaskExecutor: defined by method 'applicationTaskExecutor' in class path resource [org/springframework/boot/autoconfigure/task/TaskExecutionAutoConfiguration.class]
- taskScheduler: defined by method 'taskScheduler' in class path resource [org/springframework/boot/autoconfigure/task/TaskSchedulingAutoConfiguration.class]
I order to avoid the error you must use @Qualifier
for all injected @Executor
instances - which is definitely against Boot philosophy of "smart default".
If the TaskSchedulingAutoConfiguration
was executed before TaskExecutionAutoConfiguration
then the Executor
would be already created and TaskExecutorAutoConfiguration
would be skipped.
Since the ThreadPoolTaskExecutor
is completely inferior to ThreadPoolTaskScheduler
you should consider if it can be deprecated and ThreadPoolTaskScheduler
could be created when application needs Executor
.
If you want to retain ThreadPoolTaskExecutor
and keep the order then you should at least annotate the ThreadPoolTaskExecutor
with @Primary
. This was suggested in #15747, but it was closed without resolving the issue.
The issue was actually reported in #15729, but it was closed in favor of #15748. (And many others).
The issues were generally solved by forcing the code to prefer "applicationTaskExecutor", but that does not help regular users that require Executor for their own purpose. The fact is that adding TaskExecutionAutoConfiguration in 2.1 introduced end user issues that were not resolved properly.
What about making ThreadPoolTaskScheduler
to implement only TaskScheduler
interface by delegating to ThreadPoolTaskExecutor
? After all @EnableScheduling
only needs TaskScheduler
.
This is minimal application that will fail:
@SpringBootApplication
@EnableScheduling
public class Main {
public static void main(String[] args) throws Exception {
SpringApplication.run(Main.class, args).close();
}
@Autowired Executor executor;
}