Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AsyncTaskExecutor beans not created if any bean of type Executor is present #44659

Open
blake-bauman opened this issue Mar 10, 2025 · 6 comments
Labels
for: team-meeting An issue we'd like to discuss as a team to make progress status: waiting-for-triage An issue we've not yet triaged

Comments

@blake-bauman
Copy link

There appears to be a bug in TaskExecutorConfiguration when creating an AsyncTaskExecutor bean, the condition is:

@ConditionalOnMissingBean(Executor.class)

instead of on AsyncTaskExecutor.

The result is, if an app creates a bean of type Executor (such as a ScheduledExecutorService), this results in Spring’s default AsyncTaskExecutor beans to not be created. The application will fail to start because of the missing beans.

This is for apps trying to use the bean generated for the configuration with the Task Execution and Scheduling feature. If an app creates a bean of type Executor, the above bean never gets created.

@blake-bauman
Copy link
Author

I've attached a sample project which reproduced the issue. If you run it, you'll see it fails to start due to a missing AsyncTaskExecutor bean. If you comment out the ScheduledExecutorService bean, then it succeeds.

test-spring-task-executors.tar.gz

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Mar 10, 2025
@quaff
Copy link
Contributor

quaff commented Mar 11, 2025

I think @Bean method level @ConditionalOnMissingBean should be used instead of class level @ConditionalOnMissingBean(Executor.class).

It should only back off for TaskExecutor not Executor.

My proposal: https://github.com/spring-projects/spring-boot/compare/main...quaff:patch-137?expand=1

quaff added a commit to quaff/spring-boot that referenced this issue Mar 11, 2025
TaskExecutor will back off for custom Executor before this commit, now it only back off for custom TaskExecutor.

Fix spring-projectsGH-44659

Signed-off-by: Yanming Zhou <[email protected]>
quaff added a commit to quaff/spring-boot that referenced this issue Mar 11, 2025
TaskExecutor will back off for custom Executor before this commit, now it only back off for custom TaskExecutor.

Fix spring-projectsGH-44659

Signed-off-by: Yanming Zhou <[email protected]>
@quaff
Copy link
Contributor

quaff commented Mar 11, 2025

Or @ConditionalOnMissingBean(value =Executor.class, ignored = ScheduledExecutorService.class) ?

It make sense since ScheduledExecutorService should be used for scheduling task only.

@snicoll
Copy link
Member

snicoll commented Mar 11, 2025

It should only back off for TaskExecutor not Executor.

I disagree with that and this bit of the auto-configuration is a bit tricky, I reckon. This is due to the bridge we had before the executor abstraction was available in the JDK. The condition on Executor is on purpose as users may define such a bean and expects the auto-configuration to back-off.

@snicoll snicoll added for: team-attention An issue we'd like other members of the team to review for: team-meeting An issue we'd like to discuss as a team to make progress and removed for: team-attention An issue we'd like other members of the team to review labels Mar 11, 2025
@blake-bauman
Copy link
Author

@quaff ScheduledExecutorService was just an example. Any bean of type Executor will cause an app to fail startup if they need the AsyncTaskExecutor.

users may define such a bean and expects the auto-configuration to back-off

@snicoll We've found the opposite. Users define a bean for other purposes but expect to still use the AsyncTaskExecutor for other tasks.

@nosan
Copy link
Contributor

nosan commented Mar 11, 2025

@blake-bauman

Users define a bean for other purposes but expect to still use the AsyncTaskExecutor for other tasks.

In this case, you can use defaultCandidate=false annotation attribute and inject your ScheduledExecutorService using the @Qualifier annotation.
Here's an example based on what you shared earlier, with a slight adjustment:

@Bean(defaultCandidate = false)
@Qualifier("myexecutor")
public ScheduledExecutorService executorService() {
    return Executors.newSingleThreadScheduledExecutor();
}

This way, you will have both the auto-configured Executor and your custom ScheduledExecutorService.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
for: team-meeting An issue we'd like to discuss as a team to make progress status: waiting-for-triage An issue we've not yet triaged
Projects
None yet
Development

No branches or pull requests

5 participants