Skip to content

Commit 283dc37

Browse files
committed
Make AnnotatedControllerConfigurer use applicationTaskExecutor
Closes gh-36388
1 parent c6e47b8 commit 283dc37

File tree

3 files changed

+46
-3
lines changed

3 files changed

+46
-3
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/GraphQlAutoConfiguration.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.Arrays;
2222
import java.util.Collections;
2323
import java.util.List;
24+
import java.util.concurrent.Executor;
2425

2526
import graphql.GraphQL;
2627
import graphql.execution.instrumentation.Instrumentation;
@@ -33,10 +34,12 @@
3334
import org.springframework.aot.hint.RuntimeHintsRegistrar;
3435
import org.springframework.beans.factory.ListableBeanFactory;
3536
import org.springframework.beans.factory.ObjectProvider;
37+
import org.springframework.beans.factory.annotation.Qualifier;
3638
import org.springframework.boot.autoconfigure.AutoConfiguration;
3739
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
3840
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
3941
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
42+
import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration;
4043
import org.springframework.boot.context.properties.EnableConfigurationProperties;
4144
import org.springframework.boot.convert.ApplicationConversionService;
4245
import org.springframework.context.annotation.Bean;
@@ -152,10 +155,12 @@ public ExecutionGraphQlService executionGraphQlService(GraphQlSource graphQlSour
152155

153156
@Bean
154157
@ConditionalOnMissingBean
155-
public AnnotatedControllerConfigurer annotatedControllerConfigurer() {
158+
public AnnotatedControllerConfigurer annotatedControllerConfigurer(
159+
@Qualifier(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME) ObjectProvider<Executor> executorProvider) {
156160
AnnotatedControllerConfigurer controllerConfigurer = new AnnotatedControllerConfigurer();
157161
controllerConfigurer
158162
.addFormatterRegistrar((registry) -> ApplicationConversionService.addBeans(registry, this.beanFactory));
163+
executorProvider.ifAvailable(controllerConfigurer::setExecutor);
159164
return controllerConfigurer;
160165
}
161166

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/GraphQlAutoConfigurationTests.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.boot.autoconfigure.graphql;
1818

1919
import java.nio.charset.StandardCharsets;
20+
import java.util.concurrent.Executor;
2021

2122
import graphql.GraphQL;
2223
import graphql.execution.instrumentation.ChainedInstrumentation;
@@ -34,6 +35,7 @@
3435
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
3536
import org.springframework.boot.autoconfigure.AutoConfigurations;
3637
import org.springframework.boot.autoconfigure.graphql.GraphQlAutoConfiguration.GraphQlResourcesRuntimeHints;
38+
import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration;
3739
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
3840
import org.springframework.context.annotation.Bean;
3941
import org.springframework.context.annotation.Configuration;
@@ -209,6 +211,26 @@ void shouldContributeConnectionTypeDefinitionConfigurer() {
209211
});
210212
}
211213

214+
@Test
215+
void whenApplicationTaskExecutorIsDefinedThenAnnotatedControllerConfigurerShouldUseIt() {
216+
this.contextRunner.withConfiguration(AutoConfigurations.of(TaskExecutionAutoConfiguration.class))
217+
.run((context) -> {
218+
AnnotatedControllerConfigurer annotatedControllerConfigurer = context
219+
.getBean(AnnotatedControllerConfigurer.class);
220+
assertThat(annotatedControllerConfigurer).extracting("executor")
221+
.isSameAs(context.getBean("applicationTaskExecutor"));
222+
});
223+
}
224+
225+
@Test
226+
void whenCustomExecutorIsDefinedThenAnnotatedControllerConfigurerDoesNotUseIt() {
227+
this.contextRunner.withUserConfiguration(CustomExecutorConfiguration.class).run((context) -> {
228+
AnnotatedControllerConfigurer annotatedControllerConfigurer = context
229+
.getBean(AnnotatedControllerConfigurer.class);
230+
assertThat(annotatedControllerConfigurer).extracting("executor").isNull();
231+
});
232+
}
233+
212234
@Configuration(proxyBeanMethods = false)
213235
static class CustomGraphQlBuilderConfiguration {
214236

@@ -294,4 +316,14 @@ public void customize(GraphQlSource.SchemaResourceBuilder builder) {
294316

295317
}
296318

319+
@Configuration(proxyBeanMethods = false)
320+
static class CustomExecutorConfiguration {
321+
322+
@Bean
323+
Executor customExecutor() {
324+
return mock(Executor.class);
325+
}
326+
327+
}
328+
297329
}

spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/task-execution-and-scheduling.adoc

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,17 @@
33
In the absence of an `Executor` bean in the context, Spring Boot auto-configures an `AsyncTaskExecutor`.
44
When virtual threads are enabled (using Java 21+ and configprop:spring.threads.virtual.enabled[] set to `true`) this will be a `SimpleAsyncTaskExecutor` that uses virtual threads.
55
Otherwise, it will be a `ThreadPoolTaskExecutor` with sensible defaults.
6-
In either case, the auto-configured executor will be automatically used for asynchronous task execution (`@EnableAsync`), Spring MVC asynchronous request processing, and Spring WebFlux blocking execution.
6+
In either case, the auto-configured executor will be automatically used for:
7+
8+
- asynchronous task execution (`@EnableAsync`)
9+
- Spring for GraphQL's asynchronous handling of `Callable` return values from controller methods
10+
- Spring MVC's asynchronous request processing
11+
- Spring WebFlux's blocking execution support
712

813
[TIP]
914
====
10-
If you have defined a custom `Executor` in the context, regular task execution (that is `@EnableAsync`) will use it transparently but the Spring MVC and Spring WebFlux support will not be configured as they require an `AsyncTaskExecutor` implementation (named `applicationTaskExecutor`).
15+
If you have defined a custom `Executor` in the context, both regular task execution (that is `@EnableAsync`) and Spring for GraphQL will use it.
16+
However, the Spring MVC and Spring WebFlux support will only use it if it is an `AsyncTaskExecutor` implementation (named `applicationTaskExecutor`).
1117
Depending on your target arrangement, you could change your `Executor` into an `AsyncTaskExecutor` or define both an `AsyncTaskExecutor` and an `AsyncConfigurer` wrapping your custom `Executor`.
1218
1319
The auto-configured `TaskExecutorBuilder` allows you to easily create instances that reproduce what the auto-configuration does by default.

0 commit comments

Comments
 (0)