membersound
membersound

Reputation: 86747

How to create additional TaskExecutor beside TaskExecutionAutoConfiguration?

I want spring to load the default ThreadPoolTaskExecutor from TaskExecutionAutoConfiguration. Though I want to provide may own additional executor for some explicit side-tasks:

@Bean
public ThreadPoolExecutor myRequestPool() {
    return (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
}

Problem: adding the bean above, the TaskExecutionAutoConfiguration will not be executed anymore, and the spring-default executor will not be initialized, because @ConditionalOnMissingBean(Executor.class) does not match anymore:

package org.springframework.boot.autoconfigure.task;

public class TaskExecutionAutoConfiguration {

    @Lazy
    @Bean(name = APPLICATION_TASK_EXECUTOR_BEAN_NAME)
    @ConditionalOnMissingBean(Executor.class)
    public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) {
        return builder.build();
    }

Question: how can I still let spring create both beans?

Upvotes: 4

Views: 4588

Answers (2)

membersound
membersound

Reputation: 86747

Important: Without the following "hack", Springs default behavior here is to simply hard replace the default executor with your custom one. And as a result, also execute any normal @Async methods with your custom executor. This may mostly have undesired results.

The solution is to provide the default applicationTaskExecutor explicit, like in TaskExecutionAutoConfiguration. But without the @ConditionalOnMissingBean annotation, as follows:

@Configuration
public class AppConfig {
    @Lazy
    @Bean(name = { TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME,
            AsyncAnnotationBeanPostProcessor.DEFAULT_TASK_EXECUTOR_BEAN_NAME })
    public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) {
        return builder.build();
    }
}

This way, the default executor is always loaded, and additional custom executors can be added to the application without replacing it (which is the default behavior).

As a result, all @Async methods will still execute with the default applicationTaskExecutor, and all @Async("customExecutor") will use your custom one.

Upvotes: 1

Ken Chan
Ken Chan

Reputation: 90457

The executor bean in TaskExecutionAutoConfiguration will only be created if no other executor beans exist (due to @ConditionalOnMissingBean(Executor.class)) at the moment when processing that auto-configuration . So , in order to create both of our executor and the one defined in TaskExecutionAutoConfiguration , we need to make sure our bean is processed after TaskExecutionAutoConfiguration

According to docs , if we make our bean to be the auto-configuration candidates (which requires adding the @Configuration class in META-INF/spring.factories), we can then use @AutoConfigureAfter to configure it to be processed after TaskExecutionAutoConfiguration :

package foo.bar.baz.qux;

@Configuration
@AutoConfigureAfter(TaskExecutionAutoConfiguration.class)
public class Config {
    @Bean
    public ThreadPoolExecutor myRequestPool() {
        return (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
    }
}

Then create META-INF/spring.factories which contains :

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  foo.bar.baz.qux.Config

Upvotes: 5

Related Questions