Skip to content

Commit 3a8b2e4

Browse files
committed
Add HttpClientAutoConfiguration and use it wherever possible
Add a new `HttpClientAutoConfiguration` class that provides `ClientHttpRequestFactoryBuilder` and `ClientHttpRequestFactorySettings` beans and new configuration properties. The existing `RestTemplate`, `RestClient` and `WebServiceTemplate` auto-configurations have been updated to make use of the new HTTP client support. Users may now set `spring.http.client` property to globally change the `ClientHttpRequestFactory` used in their application. Closes gh-36266
1 parent 6356e90 commit 3a8b2e4

File tree

21 files changed

+600
-46
lines changed

21 files changed

+600
-46
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright 2012-2024 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.http.client;
18+
19+
import org.springframework.beans.factory.ObjectProvider;
20+
import org.springframework.boot.autoconfigure.AutoConfiguration;
21+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
22+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
23+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
24+
import org.springframework.boot.autoconfigure.http.client.HttpClientProperties.Factory;
25+
import org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration;
26+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
27+
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
28+
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
29+
import org.springframework.boot.ssl.SslBundle;
30+
import org.springframework.boot.ssl.SslBundles;
31+
import org.springframework.context.annotation.Bean;
32+
import org.springframework.context.annotation.Conditional;
33+
import org.springframework.http.client.ClientHttpRequestFactory;
34+
import org.springframework.util.StringUtils;
35+
36+
/**
37+
* {@link EnableAutoConfiguration Auto-configuration} for
38+
* {@link ClientHttpRequestFactoryBuilder} and {@link ClientHttpRequestFactorySettings}.
39+
*
40+
* @author Phillip Webb
41+
* @since 3.4.0
42+
*/
43+
@AutoConfiguration(after = SslAutoConfiguration.class)
44+
@ConditionalOnClass(ClientHttpRequestFactory.class)
45+
@Conditional(NotReactiveWebApplicationCondition.class)
46+
@EnableConfigurationProperties(HttpClientProperties.class)
47+
public class HttpClientAutoConfiguration {
48+
49+
@Bean
50+
@ConditionalOnMissingBean
51+
ClientHttpRequestFactoryBuilder<?> clientHttpRequestFactoryBuilder(HttpClientProperties httpClientProperties) {
52+
Factory factory = httpClientProperties.getFactory();
53+
return (factory != null) ? factory.builder() : ClientHttpRequestFactoryBuilder.detect();
54+
}
55+
56+
@Bean
57+
@ConditionalOnMissingBean
58+
ClientHttpRequestFactorySettings clientHttpRequestFactorySettings(HttpClientProperties httpClientProperties,
59+
ObjectProvider<SslBundles> sslBundles) {
60+
SslBundle sslBundle = getSslBundle(httpClientProperties.getSsl(), sslBundles);
61+
return new ClientHttpRequestFactorySettings(httpClientProperties.getConnectTimeout(),
62+
httpClientProperties.getReadTimeout(), sslBundle);
63+
}
64+
65+
private SslBundle getSslBundle(HttpClientProperties.Ssl properties, ObjectProvider<SslBundles> sslBundles) {
66+
String name = properties.getBundle();
67+
return (StringUtils.hasLength(name)) ? sslBundles.getObject().getBundle(name) : null;
68+
}
69+
70+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/*
2+
* Copyright 2012-2024 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.http.client;
18+
19+
import java.time.Duration;
20+
import java.util.function.Supplier;
21+
22+
import org.springframework.boot.context.properties.ConfigurationProperties;
23+
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
24+
25+
/**
26+
* {@link ConfigurationProperties @ConfigurationProperties} for a Spring's blocking HTTP
27+
* clients.
28+
*
29+
* @author Phillip Webb
30+
* @since 3.4.0
31+
*/
32+
@ConfigurationProperties("spring.http.client")
33+
public class HttpClientProperties {
34+
35+
/**
36+
* Default factory used for a client HTTP request.
37+
*/
38+
private Factory factory;
39+
40+
/**
41+
* Default connect timeout for a client HTTP request.
42+
*/
43+
private Duration connectTimeout;
44+
45+
/**
46+
* Default read timeout for a client HTTP request.
47+
*/
48+
private Duration readTimeout;
49+
50+
/**
51+
* Default SSL configuration for a client HTTP request.
52+
*/
53+
private Ssl ssl = new Ssl();
54+
55+
public Factory getFactory() {
56+
return this.factory;
57+
}
58+
59+
public void setFactory(Factory factory) {
60+
this.factory = factory;
61+
}
62+
63+
public Duration getConnectTimeout() {
64+
return this.connectTimeout;
65+
}
66+
67+
public void setConnectTimeout(Duration connectTimeout) {
68+
this.connectTimeout = connectTimeout;
69+
}
70+
71+
public Duration getReadTimeout() {
72+
return this.readTimeout;
73+
}
74+
75+
public void setReadTimeout(Duration readTimeout) {
76+
this.readTimeout = readTimeout;
77+
}
78+
79+
public Ssl getSsl() {
80+
return this.ssl;
81+
}
82+
83+
/**
84+
* Supported factory types.
85+
*/
86+
public enum Factory {
87+
88+
/**
89+
* Apache HttpComponents HttpClient.
90+
*/
91+
HTTP_COMPONENTS(ClientHttpRequestFactoryBuilder::httpComponents),
92+
93+
/**
94+
* Jetty's HttpClient.
95+
*/
96+
JETTY(ClientHttpRequestFactoryBuilder::jetty),
97+
98+
/**
99+
* Reactor-Netty.
100+
*/
101+
REACTOR(ClientHttpRequestFactoryBuilder::reactor),
102+
103+
/**
104+
* Java's HttpClient.
105+
*/
106+
JDK(ClientHttpRequestFactoryBuilder::jdk),
107+
108+
/**
109+
* Standard JDK facilities.
110+
*/
111+
SIMPLE(ClientHttpRequestFactoryBuilder::simple);
112+
113+
private final Supplier<ClientHttpRequestFactoryBuilder<?>> builderSupplier;
114+
115+
Factory(Supplier<ClientHttpRequestFactoryBuilder<?>> builderSupplier) {
116+
this.builderSupplier = builderSupplier;
117+
}
118+
119+
ClientHttpRequestFactoryBuilder<?> builder() {
120+
return this.builderSupplier.get();
121+
}
122+
123+
}
124+
125+
/**
126+
* SSL configuration.
127+
*/
128+
public static class Ssl {
129+
130+
/**
131+
* SSL bundle to use.
132+
*/
133+
private String bundle;
134+
135+
public String getBundle() {
136+
return this.bundle;
137+
}
138+
139+
public void setBundle(String bundle) {
140+
this.bundle = bundle;
141+
}
142+
143+
}
144+
145+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2012-2024 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.http.client;
18+
19+
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
20+
import org.springframework.boot.autoconfigure.condition.NoneNestedConditions;
21+
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
22+
23+
/**
24+
* {@link SpringBootCondition} that applies only when running in a non-reactive web
25+
* application.
26+
*
27+
* @author Phillip Webb
28+
*/
29+
class NotReactiveWebApplicationCondition extends NoneNestedConditions {
30+
31+
NotReactiveWebApplicationCondition() {
32+
super(ConfigurationPhase.PARSE_CONFIGURATION);
33+
}
34+
35+
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
36+
private static final class ReactiveWebApplication {
37+
38+
}
39+
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright 2012-2024 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+
/**
18+
* Auto-configuration for client-side HTTP.
19+
*/
20+
package org.springframework.boot.autoconfigure.http.client;

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/AutoConfiguredRestClientSsl.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,10 +18,10 @@
1818

1919
import java.util.function.Consumer;
2020

21+
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
22+
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
2123
import org.springframework.boot.ssl.SslBundle;
2224
import org.springframework.boot.ssl.SslBundles;
23-
import org.springframework.boot.web.client.ClientHttpRequestFactories;
24-
import org.springframework.boot.web.client.ClientHttpRequestFactorySettings;
2525
import org.springframework.http.client.ClientHttpRequestFactory;
2626
import org.springframework.web.client.RestClient;
2727

@@ -32,9 +32,13 @@
3232
*/
3333
class AutoConfiguredRestClientSsl implements RestClientSsl {
3434

35+
private final ClientHttpRequestFactoryBuilder<?> clientHttpRequestFactoryBuilder;
36+
3537
private final SslBundles sslBundles;
3638

37-
AutoConfiguredRestClientSsl(SslBundles sslBundles) {
39+
AutoConfiguredRestClientSsl(ClientHttpRequestFactoryBuilder<?> clientHttpRequestFactoryBuilder,
40+
SslBundles sslBundles) {
41+
this.clientHttpRequestFactoryBuilder = clientHttpRequestFactoryBuilder;
3842
this.sslBundles = sslBundles;
3943
}
4044

@@ -46,8 +50,8 @@ public Consumer<RestClient.Builder> fromBundle(String bundleName) {
4650
@Override
4751
public Consumer<RestClient.Builder> fromBundle(SslBundle bundle) {
4852
return (builder) -> {
49-
ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.DEFAULTS.withSslBundle(bundle);
50-
ClientHttpRequestFactory requestFactory = ClientHttpRequestFactories.get(settings);
53+
ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.ofSslBundle(bundle);
54+
ClientHttpRequestFactory requestFactory = this.clientHttpRequestFactoryBuilder.build(settings);
5155
builder.requestFactory(requestFactory);
5256
};
5357
}

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

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@
2424
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2525
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
2626
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
27+
import org.springframework.boot.autoconfigure.http.client.HttpClientAutoConfiguration;
2728
import org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration;
29+
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
30+
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
2831
import org.springframework.boot.ssl.SslBundles;
29-
import org.springframework.boot.web.client.ClientHttpRequestFactories;
30-
import org.springframework.boot.web.client.ClientHttpRequestFactorySettings;
3132
import org.springframework.boot.web.client.RestClientCustomizer;
3233
import org.springframework.context.annotation.Bean;
3334
import org.springframework.context.annotation.Conditional;
@@ -48,7 +49,8 @@
4849
* @author Moritz Halbritter
4950
* @since 3.2.0
5051
*/
51-
@AutoConfiguration(after = { HttpMessageConvertersAutoConfiguration.class, SslAutoConfiguration.class })
52+
@AutoConfiguration(after = { HttpClientAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class,
53+
SslAutoConfiguration.class })
5254
@ConditionalOnClass(RestClient.class)
5355
@Conditional(NotReactiveWebApplicationCondition.class)
5456
public class RestClientAutoConfiguration {
@@ -64,14 +66,21 @@ HttpMessageConvertersRestClientCustomizer httpMessageConvertersRestClientCustomi
6466
@Bean
6567
@ConditionalOnMissingBean(RestClientSsl.class)
6668
@ConditionalOnBean(SslBundles.class)
67-
AutoConfiguredRestClientSsl restClientSsl(SslBundles sslBundles) {
68-
return new AutoConfiguredRestClientSsl(sslBundles);
69+
AutoConfiguredRestClientSsl restClientSsl(
70+
ObjectProvider<ClientHttpRequestFactoryBuilder<?>> clientHttpRequestFactoryBuilder, SslBundles sslBundles) {
71+
return new AutoConfiguredRestClientSsl(
72+
clientHttpRequestFactoryBuilder.getIfAvailable(ClientHttpRequestFactoryBuilder::detect), sslBundles);
6973
}
7074

7175
@Bean
7276
@ConditionalOnMissingBean
73-
RestClientBuilderConfigurer restClientBuilderConfigurer(ObjectProvider<RestClientCustomizer> customizerProvider) {
77+
RestClientBuilderConfigurer restClientBuilderConfigurer(
78+
ObjectProvider<ClientHttpRequestFactoryBuilder<?>> clientHttpRequestFactoryBuilder,
79+
ObjectProvider<ClientHttpRequestFactorySettings> clientHttpRequestFactorySettings,
80+
ObjectProvider<RestClientCustomizer> customizerProvider) {
7481
RestClientBuilderConfigurer configurer = new RestClientBuilderConfigurer();
82+
configurer.setRequestFactoryBuilder(clientHttpRequestFactoryBuilder.getIfAvailable());
83+
configurer.setRequestFactorySettings(clientHttpRequestFactorySettings.getIfAvailable());
7584
configurer.setRestClientCustomizers(customizerProvider.orderedStream().toList());
7685
return configurer;
7786
}
@@ -80,9 +89,7 @@ RestClientBuilderConfigurer restClientBuilderConfigurer(ObjectProvider<RestClien
8089
@Scope("prototype")
8190
@ConditionalOnMissingBean
8291
RestClient.Builder restClientBuilder(RestClientBuilderConfigurer restClientBuilderConfigurer) {
83-
RestClient.Builder builder = RestClient.builder()
84-
.requestFactory(ClientHttpRequestFactories.get(ClientHttpRequestFactorySettings.DEFAULTS));
85-
return restClientBuilderConfigurer.configure(builder);
92+
return restClientBuilderConfigurer.configure(RestClient.builder());
8693
}
8794

8895
}

0 commit comments

Comments
 (0)