diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfiguration.java index 0d7ae4eb49d..be7e3e3dda4 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfiguration.java @@ -34,6 +34,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat import org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration; import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration; import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProviders; +import org.springframework.boot.autoconfigure.thread.Threading; import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration; import org.springframework.boot.autoconfigure.validation.ValidatorAdapter; import org.springframework.boot.autoconfigure.web.ConditionalOnEnabledResourceChain; @@ -55,6 +56,7 @@ import org.springframework.context.annotation.Import; import org.springframework.context.annotation.ImportRuntimeHints; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; +import org.springframework.core.env.Environment; import org.springframework.core.task.AsyncTaskExecutor; import org.springframework.format.FormatterRegistry; import org.springframework.format.support.FormattingConversionService; @@ -149,6 +151,8 @@ public class WebFluxAutoConfiguration { private static final Log logger = LogFactory.getLog(WebFluxConfig.class); + private final Environment environment; + private final Resources resourceProperties; private final WebFluxProperties webFluxProperties; @@ -163,11 +167,12 @@ public class WebFluxAutoConfiguration { private final ObjectProvider viewResolvers; - public WebFluxConfig(WebProperties webProperties, WebFluxProperties webFluxProperties, + public WebFluxConfig(Environment environment, WebProperties webProperties, WebFluxProperties webFluxProperties, ListableBeanFactory beanFactory, ObjectProvider resolvers, ObjectProvider codecCustomizers, ObjectProvider resourceHandlerRegistrationCustomizer, ObjectProvider viewResolvers) { + this.environment = environment; this.resourceProperties = webProperties.getResources(); this.webFluxProperties = webFluxProperties; this.beanFactory = beanFactory; @@ -189,7 +194,8 @@ public class WebFluxAutoConfiguration { @Override public void configureBlockingExecution(BlockingExecutionConfigurer configurer) { - if (this.beanFactory.containsBean(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME)) { + if (Threading.VIRTUAL.isActive(this.environment) && this.beanFactory + .containsBean(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME)) { Object taskExecutor = this.beanFactory .getBean(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME); if (taskExecutor instanceof AsyncTaskExecutor asyncTaskExecutor) { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfigurationTests.java index b6a15e1ae4d..27ae96ee440 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfigurationTests.java @@ -39,6 +39,8 @@ import org.aspectj.lang.annotation.Aspect; import org.assertj.core.api.Assertions; import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledForJreRange; +import org.junit.jupiter.api.condition.JRE; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -704,8 +706,20 @@ class WebFluxAutoConfigurationTests { } @Test - void asyncTaskExecutorWithApplicationTaskExecutor() { + void asyncTaskExecutorWithPlatformThreadsAndApplicationTaskExecutor() { this.contextRunner.withConfiguration(AutoConfigurations.of(TaskExecutionAutoConfiguration.class)) + .run((context) -> { + assertThat(context).hasSingleBean(AsyncTaskExecutor.class); + assertThat(context.getBean(RequestMappingHandlerAdapter.class)).extracting("scheduler.executor") + .isNull(); + }); + } + + @Test + @EnabledForJreRange(min = JRE.JAVA_21) + void asyncTaskExecutorWithVirtualThreadsAndApplicationTaskExecutor() { + this.contextRunner.withPropertyValues("spring.threads.virtual.enabled=true") + .withConfiguration(AutoConfigurations.of(TaskExecutionAutoConfiguration.class)) .run((context) -> { assertThat(context).hasSingleBean(AsyncTaskExecutor.class); assertThat(context.getBean(RequestMappingHandlerAdapter.class)).extracting("scheduler.executor") @@ -714,8 +728,10 @@ class WebFluxAutoConfigurationTests { } @Test - void asyncTaskExecutorWithNonMatchApplicationTaskExecutorBean() { - this.contextRunner.withUserConfiguration(CustomApplicationTaskExecutorConfig.class) + @EnabledForJreRange(min = JRE.JAVA_21) + void asyncTaskExecutorWithVirtualThreadsAndNonMatchApplicationTaskExecutorBean() { + this.contextRunner.withPropertyValues("spring.threads.virtual.enabled=true") + .withUserConfiguration(CustomApplicationTaskExecutorConfig.class) .withConfiguration(AutoConfigurations.of(TaskExecutionAutoConfiguration.class)) .run((context) -> { assertThat(context).doesNotHaveBean(AsyncTaskExecutor.class); @@ -725,8 +741,10 @@ class WebFluxAutoConfigurationTests { } @Test - void asyncTaskExecutorWithWebFluxConfigurerCanOverrideExecutor() { - this.contextRunner.withUserConfiguration(CustomAsyncTaskExecutorConfigurer.class) + @EnabledForJreRange(min = JRE.JAVA_21) + void asyncTaskExecutorWithVirtualThreadsAndWebFluxConfigurerCanOverrideExecutor() { + this.contextRunner.withPropertyValues("spring.threads.virtual.enabled=true") + .withUserConfiguration(CustomAsyncTaskExecutorConfigurer.class) .withConfiguration(AutoConfigurations.of(TaskExecutionAutoConfiguration.class)) .run((context) -> assertThat(context.getBean(RequestMappingHandlerAdapter.class)) .extracting("scheduler.executor") @@ -734,13 +752,15 @@ class WebFluxAutoConfigurationTests { } @Test - void asyncTaskExecutorWithCustomNonApplicationTaskExecutor() { - this.contextRunner.withUserConfiguration(CustomAsyncTaskExecutorConfig.class) + @EnabledForJreRange(min = JRE.JAVA_21) + void asyncTaskExecutorWithVirtualThreadsAndCustomNonApplicationTaskExecutor() { + this.contextRunner.withPropertyValues("spring.threads.virtual.enabled=true") + .withUserConfiguration(CustomAsyncTaskExecutorConfig.class) .withConfiguration(AutoConfigurations.of(TaskExecutionAutoConfiguration.class)) .run((context) -> { assertThat(context).hasSingleBean(AsyncTaskExecutor.class); assertThat(context.getBean(RequestMappingHandlerAdapter.class)).extracting("scheduler.executor") - .isNotSameAs(context.getBean("customTaskExecutor")); + .isNull(); }); }