TaskExecutionAutoConfiguration.applicationTaskExecutor
is conditional on missing bean java.util.concurrent.Executor
. This results in a java.util.concurrent.ThreadPoolExecutor
bean stopping instantiation of the applicationTaskExecutor
.
https://github.com/spring-projects/spring-boot/blob/1bca2bbc859b0a40b6b5616c0b6433e6047c36ca/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutionAutoConfiguration.java#L75-L81
However, a ThreadPoolExecutor
is not a Spring TaskExecutor
. So eventually (somewhere) a stop-gap SimpleAsyncTaskExecutor
is created, and then warnings start getting logged:
!!!
An Executor is required to handle java.util.concurrent.Callable return values.
Please, configure a TaskExecutor in the MVC config under "async support".
The SimpleAsyncTaskExecutor currently in use is not suitable under load.
-------------------------------
Request URI: '/api/whatever'
!!!
I believe there is an implicit assumption here that any Executor
bean defined would naturally be a Spring TaskExecutor
, because it's a Spring program. This seems to me to be a bad assumption.
I propose one of the following:
- Make the
applicationTaskExecutor
bean conditional onTaskExecutor
instead of justExecutor
. That way, otherExecutor
orExecutorService
beans which are not alsoTaskExecutor
will not prevent theapplicationTaskExecutor
from being created. - If an
Executor
is defined, create aTaskExecutor
wrapping the given executor. If anExecutorService
is defined, anAsyncTaskExecutor
wrapper could be created instead. Based on the interfaces, these wrappers would be very simple pass-through implementations, as the Spring interfaces are basically subsets of the Java ones.
Happy to help with pull requests, but I thought this warranted some discussion first.
Comment From: jdmichal
Ends up the wrapper necessary for (2) already exists: org.springframework.core.task.support.TaskExecutorAdapter
.
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/task/support/TaskExecutorAdapter.html
That said, I want to clarify that I would prefer solution (1), because users might legitimately want to define additional Executor
and ExecutorService
beans without replacing the default TaskExecutor
.
Comment From: snicoll
@jdmichal thanks for the report but the condition is set on Executor
on purpose. The application task executor is used for async processing (@EnableAsync
) and asynchronous MVC handling. The former requires an Executor
while the latter requires an AsyncTaskExecutor
.
If you're not using any of the MVC async feature, using an Executor
works just fine and asynchronous processing will use the executor that you've defined. If you do however, it is important to either provide a tailored AsyncTaskExecutor
or let Spring Boot configure one for you.
Doing what you've described would force users of async processing to provide an implementation that isn't actually necessary. I understand this is not ideal but the warning is useful to let you know that your custom executor is not considered and that something needs to be fixed. Creating two Executors
behind the scenes is something we're not keen to do, hence the current situation.
Finally, there is an explicit note in the documentation about this.