Skip to content

Commit f77f3c6

Browse files
committed
Merge pull request #44952 from dmitrysulman
* pr/44952: Polish 'Auto-configure rest client when virtual threads are enabled' Auto-configure rest client when virtual threads are enabled Closes gh-44952
2 parents 2f30c52 + ff3b495 commit f77f3c6

File tree

3 files changed

+110
-2
lines changed

3 files changed

+110
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright 2012-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.web.client;
18+
19+
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
20+
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
21+
import org.springframework.boot.autoconfigure.condition.ConditionalOnThreading;
22+
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
23+
import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration;
24+
import org.springframework.boot.autoconfigure.thread.Threading;
25+
import org.springframework.context.annotation.Conditional;
26+
27+
/**
28+
* {@link SpringBootCondition} that applies when running in a non-reactive web application
29+
* or virtual threads are enabled.
30+
*
31+
* @author Dmitry Sulman
32+
*/
33+
class NotReactiveWebApplicationOrVirtualThreadsExecutorEnabledCondition extends AnyNestedCondition {
34+
35+
NotReactiveWebApplicationOrVirtualThreadsExecutorEnabledCondition() {
36+
super(ConfigurationPhase.REGISTER_BEAN);
37+
}
38+
39+
@Conditional(NotReactiveWebApplicationCondition.class)
40+
private static final class NotReactiveWebApplication {
41+
42+
}
43+
44+
@ConditionalOnThreading(Threading.VIRTUAL)
45+
@ConditionalOnBean(name = TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME)
46+
private static final class VirtualThreadsEnabled {
47+
48+
}
49+
50+
}

Diff for: spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/RestClientAutoConfiguration.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
2828
import org.springframework.boot.autoconfigure.http.client.HttpClientAutoConfiguration;
2929
import org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration;
30+
import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration;
3031
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
3132
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
3233
import org.springframework.boot.ssl.SslBundles;
@@ -51,9 +52,9 @@
5152
* @since 3.2.0
5253
*/
5354
@AutoConfiguration(after = { HttpClientAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class,
54-
SslAutoConfiguration.class })
55+
SslAutoConfiguration.class, TaskExecutionAutoConfiguration.class })
5556
@ConditionalOnClass(RestClient.class)
56-
@Conditional(NotReactiveWebApplicationCondition.class)
57+
@Conditional(NotReactiveWebApplicationOrVirtualThreadsExecutorEnabledCondition.class)
5758
public class RestClientAutoConfiguration {
5859

5960
@Bean

Diff for: spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/client/RestClientAutoConfigurationTests.java

+57
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,22 @@
2020
import java.util.List;
2121

2222
import org.junit.jupiter.api.Test;
23+
import org.junit.jupiter.api.condition.EnabledForJreRange;
24+
import org.junit.jupiter.api.condition.JRE;
2325

2426
import org.springframework.boot.autoconfigure.AutoConfigurations;
2527
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
2628
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
2729
import org.springframework.boot.autoconfigure.http.client.HttpClientAutoConfiguration;
30+
import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration;
2831
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
2932
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
3033
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings.Redirects;
3134
import org.springframework.boot.ssl.SslBundle;
3235
import org.springframework.boot.ssl.SslBundles;
3336
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
37+
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
38+
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
3439
import org.springframework.boot.web.client.RestClientCustomizer;
3540
import org.springframework.boot.web.codec.CodecCustomizer;
3641
import org.springframework.context.annotation.Bean;
@@ -53,6 +58,7 @@
5358
* @author Arjen Poutsma
5459
* @author Moritz Halbritter
5560
* @author Dmytro Nosan
61+
* @author Dmitry Sulman
5662
*/
5763
class RestClientAutoConfigurationTests {
5864

@@ -260,6 +266,57 @@ void shouldSupplyRestClientBuilderConfigurerWithAutoConfiguredHttpSettings() {
260266
});
261267
}
262268

269+
@Test
270+
void whenReactiveWebApplicationRestClientIsNotConfigured() {
271+
new ReactiveWebApplicationContextRunner()
272+
.withConfiguration(AutoConfigurations.of(RestClientAutoConfiguration.class))
273+
.run((context) -> {
274+
assertThat(context).doesNotHaveBean(HttpMessageConvertersRestClientCustomizer.class);
275+
assertThat(context).doesNotHaveBean(RestClientBuilderConfigurer.class);
276+
assertThat(context).doesNotHaveBean(RestClient.Builder.class);
277+
});
278+
}
279+
280+
@Test
281+
void whenServletWebApplicationRestClientIsConfigured() {
282+
new WebApplicationContextRunner().withConfiguration(AutoConfigurations.of(RestClientAutoConfiguration.class))
283+
.run((context) -> {
284+
assertThat(context).hasSingleBean(HttpMessageConvertersRestClientCustomizer.class);
285+
assertThat(context).hasSingleBean(RestClientBuilderConfigurer.class);
286+
assertThat(context).hasSingleBean(RestClient.Builder.class);
287+
});
288+
}
289+
290+
@Test
291+
@EnabledForJreRange(min = JRE.JAVA_21)
292+
void whenReactiveWebApplicationAndVirtualThreadsEnabledAndTaskExecutorBean() {
293+
new ReactiveWebApplicationContextRunner().withPropertyValues("spring.threads.virtual.enabled=true")
294+
.withConfiguration(
295+
AutoConfigurations.of(RestClientAutoConfiguration.class, TaskExecutionAutoConfiguration.class))
296+
.run((context) -> {
297+
assertThat(context).hasSingleBean(HttpMessageConvertersRestClientCustomizer.class);
298+
assertThat(context).hasSingleBean(RestClientBuilderConfigurer.class);
299+
assertThat(context).hasSingleBean(RestClient.Builder.class);
300+
});
301+
}
302+
303+
@Test
304+
@EnabledForJreRange(min = JRE.JAVA_21)
305+
void whenReactiveWebApplicationAndVirtualThreadsDisabled() {
306+
new ReactiveWebApplicationContextRunner().withPropertyValues("spring.threads.virtual.enabled=false")
307+
.withConfiguration(
308+
AutoConfigurations.of(RestClientAutoConfiguration.class, TaskExecutionAutoConfiguration.class))
309+
.run((context) -> assertThat(context).doesNotHaveBean(RestClient.Builder.class));
310+
}
311+
312+
@Test
313+
@EnabledForJreRange(min = JRE.JAVA_21)
314+
void whenReactiveWebApplicationAndVirtualThreadsEnabledAndNoTaskExecutorBean() {
315+
new ReactiveWebApplicationContextRunner().withPropertyValues("spring.threads.virtual.enabled=true")
316+
.withConfiguration(AutoConfigurations.of(RestClientAutoConfiguration.class))
317+
.run((context) -> assertThat(context).doesNotHaveBean(RestClient.Builder.class));
318+
}
319+
263320
@Configuration(proxyBeanMethods = false)
264321
static class CodecConfiguration {
265322

0 commit comments

Comments
 (0)