Make AnnotatedControllerConfigurer use applicationTaskExecutor
Closes gh-36388
This commit is contained in:
parent
c6e47b86d7
commit
283dc37db3
|
@ -21,6 +21,7 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import graphql.GraphQL;
|
||||
import graphql.execution.instrumentation.Instrumentation;
|
||||
|
@ -33,10 +34,12 @@ import org.springframework.aot.hint.RuntimeHints;
|
|||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.convert.ApplicationConversionService;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
@ -152,10 +155,12 @@ public class GraphQlAutoConfiguration {
|
|||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public AnnotatedControllerConfigurer annotatedControllerConfigurer() {
|
||||
public AnnotatedControllerConfigurer annotatedControllerConfigurer(
|
||||
@Qualifier(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME) ObjectProvider<Executor> executorProvider) {
|
||||
AnnotatedControllerConfigurer controllerConfigurer = new AnnotatedControllerConfigurer();
|
||||
controllerConfigurer
|
||||
.addFormatterRegistrar((registry) -> ApplicationConversionService.addBeans(registry, this.beanFactory));
|
||||
executorProvider.ifAvailable(controllerConfigurer::setExecutor);
|
||||
return controllerConfigurer;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.boot.autoconfigure.graphql;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import graphql.GraphQL;
|
||||
import graphql.execution.instrumentation.ChainedInstrumentation;
|
||||
|
@ -34,6 +35,7 @@ import org.springframework.aot.hint.RuntimeHints;
|
|||
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.graphql.GraphQlAutoConfiguration.GraphQlResourcesRuntimeHints;
|
||||
import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
@ -209,6 +211,26 @@ class GraphQlAutoConfigurationTests {
|
|||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenApplicationTaskExecutorIsDefinedThenAnnotatedControllerConfigurerShouldUseIt() {
|
||||
this.contextRunner.withConfiguration(AutoConfigurations.of(TaskExecutionAutoConfiguration.class))
|
||||
.run((context) -> {
|
||||
AnnotatedControllerConfigurer annotatedControllerConfigurer = context
|
||||
.getBean(AnnotatedControllerConfigurer.class);
|
||||
assertThat(annotatedControllerConfigurer).extracting("executor")
|
||||
.isSameAs(context.getBean("applicationTaskExecutor"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenCustomExecutorIsDefinedThenAnnotatedControllerConfigurerDoesNotUseIt() {
|
||||
this.contextRunner.withUserConfiguration(CustomExecutorConfiguration.class).run((context) -> {
|
||||
AnnotatedControllerConfigurer annotatedControllerConfigurer = context
|
||||
.getBean(AnnotatedControllerConfigurer.class);
|
||||
assertThat(annotatedControllerConfigurer).extracting("executor").isNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomGraphQlBuilderConfiguration {
|
||||
|
||||
|
@ -294,4 +316,14 @@ class GraphQlAutoConfigurationTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomExecutorConfiguration {
|
||||
|
||||
@Bean
|
||||
Executor customExecutor() {
|
||||
return mock(Executor.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,11 +3,17 @@
|
|||
In the absence of an `Executor` bean in the context, Spring Boot auto-configures an `AsyncTaskExecutor`.
|
||||
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.
|
||||
Otherwise, it will be a `ThreadPoolTaskExecutor` with sensible defaults.
|
||||
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.
|
||||
In either case, the auto-configured executor will be automatically used for:
|
||||
|
||||
- asynchronous task execution (`@EnableAsync`)
|
||||
- Spring for GraphQL's asynchronous handling of `Callable` return values from controller methods
|
||||
- Spring MVC's asynchronous request processing
|
||||
- Spring WebFlux's blocking execution support
|
||||
|
||||
[TIP]
|
||||
====
|
||||
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`).
|
||||
If you have defined a custom `Executor` in the context, both regular task execution (that is `@EnableAsync`) and Spring for GraphQL will use it.
|
||||
However, the Spring MVC and Spring WebFlux support will only use it if it is an `AsyncTaskExecutor` implementation (named `applicationTaskExecutor`).
|
||||
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`.
|
||||
|
||||
The auto-configured `TaskExecutorBuilder` allows you to easily create instances that reproduce what the auto-configuration does by default.
|
||||
|
|
Loading…
Reference in New Issue