Make AnnotatedControllerConfigurer use applicationTaskExecutor
Closes gh-36388
This commit is contained in:
@@ -21,6 +21,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
import graphql.GraphQL;
|
import graphql.GraphQL;
|
||||||
import graphql.execution.instrumentation.Instrumentation;
|
import graphql.execution.instrumentation.Instrumentation;
|
||||||
@@ -33,10 +34,12 @@ import org.springframework.aot.hint.RuntimeHints;
|
|||||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||||
import org.springframework.beans.factory.ListableBeanFactory;
|
import org.springframework.beans.factory.ListableBeanFactory;
|
||||||
import org.springframework.beans.factory.ObjectProvider;
|
import org.springframework.beans.factory.ObjectProvider;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
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.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.boot.convert.ApplicationConversionService;
|
import org.springframework.boot.convert.ApplicationConversionService;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
@@ -152,10 +155,12 @@ public class GraphQlAutoConfiguration {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
public AnnotatedControllerConfigurer annotatedControllerConfigurer() {
|
public AnnotatedControllerConfigurer annotatedControllerConfigurer(
|
||||||
|
@Qualifier(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME) ObjectProvider<Executor> executorProvider) {
|
||||||
AnnotatedControllerConfigurer controllerConfigurer = new AnnotatedControllerConfigurer();
|
AnnotatedControllerConfigurer controllerConfigurer = new AnnotatedControllerConfigurer();
|
||||||
controllerConfigurer
|
controllerConfigurer
|
||||||
.addFormatterRegistrar((registry) -> ApplicationConversionService.addBeans(registry, this.beanFactory));
|
.addFormatterRegistrar((registry) -> ApplicationConversionService.addBeans(registry, this.beanFactory));
|
||||||
|
executorProvider.ifAvailable(controllerConfigurer::setExecutor);
|
||||||
return controllerConfigurer;
|
return controllerConfigurer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package org.springframework.boot.autoconfigure.graphql;
|
package org.springframework.boot.autoconfigure.graphql;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
import graphql.GraphQL;
|
import graphql.GraphQL;
|
||||||
import graphql.execution.instrumentation.ChainedInstrumentation;
|
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.aot.hint.predicate.RuntimeHintsPredicates;
|
||||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||||
import org.springframework.boot.autoconfigure.graphql.GraphQlAutoConfiguration.GraphQlResourcesRuntimeHints;
|
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.boot.test.context.runner.ApplicationContextRunner;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
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)
|
@Configuration(proxyBeanMethods = false)
|
||||||
static class CustomGraphQlBuilderConfiguration {
|
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`.
|
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.
|
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.
|
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]
|
[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`.
|
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.
|
The auto-configured `TaskExecutorBuilder` allows you to easily create instances that reproduce what the auto-configuration does by default.
|
||||||
|
|||||||
Reference in New Issue
Block a user