From 256f9fe83af13066725802dd692de6dfe8dd046f Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 9 Feb 2024 18:25:51 +0000 Subject: [PATCH] Only configure WebFlux blocking executor when using virtual threads Fixes gh-39469 --- .../reactive/WebFluxAutoConfiguration.java | 12 ++++-- .../WebFluxAutoConfigurationTests.java | 38 ++++++++++++++----- 2 files changed, 38 insertions(+), 12 deletions(-) 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 e9ebef36249..cab4dd72b43 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -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 cf40c58a52c..53731f72fcf 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -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; @@ -687,8 +689,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") @@ -697,8 +711,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); @@ -708,8 +724,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") @@ -717,13 +735,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(); }); }