Skip to content

Commit 455bb0f

Browse files
committed
Add ClientHttpRequestFactoryBuilderCustomizer support
Add `ClientHttpRequestFactoryBuilderCustomizer` interface that can be used to customize the `ClientHttpRequestFactoryBuilder`. Closes gh-44987
1 parent 983e7b6 commit 455bb0f

File tree

4 files changed

+87
-4
lines changed

4 files changed

+87
-4
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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.http.client;
18+
19+
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
20+
21+
/**
22+
* Customizer that can be used to modify the auto-configured
23+
* {@link ClientHttpRequestFactoryBuilder} when its type matches.
24+
*
25+
* @param <B> the builder type
26+
* @author Phillip Webb
27+
* @since 3.5.0
28+
*/
29+
public interface ClientHttpRequestFactoryBuilderCustomizer<B extends ClientHttpRequestFactoryBuilder<?>> {
30+
31+
/**
32+
* Customize the given builder.
33+
* @param builder the builder to customize
34+
* @return the customized builder
35+
*/
36+
B customize(B builder);
37+
38+
}

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/HttpClientAutoConfiguration.java

+16-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.boot.autoconfigure.http.client;
1818

19+
import java.util.List;
20+
1921
import org.springframework.beans.factory.ObjectProvider;
2022
import org.springframework.boot.autoconfigure.AutoConfiguration;
2123
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@@ -29,6 +31,7 @@
2931
import org.springframework.boot.http.client.HttpClientSettings;
3032
import org.springframework.boot.http.client.HttpRedirects;
3133
import org.springframework.boot.ssl.SslBundles;
34+
import org.springframework.boot.util.LambdaSafe;
3235
import org.springframework.context.annotation.Bean;
3336
import org.springframework.context.annotation.Conditional;
3437
import org.springframework.http.client.ClientHttpRequestFactory;
@@ -48,8 +51,19 @@ public class HttpClientAutoConfiguration {
4851

4952
@Bean
5053
@ConditionalOnMissingBean
51-
ClientHttpRequestFactoryBuilder<?> clientHttpRequestFactoryBuilder(HttpClientProperties httpClientProperties) {
52-
return httpClientProperties.factoryBuilder();
54+
ClientHttpRequestFactoryBuilder<?> clientHttpRequestFactoryBuilder(HttpClientProperties httpClientProperties,
55+
ObjectProvider<ClientHttpRequestFactoryBuilderCustomizer<?>> clientHttpRequestFactoryBuilderCustomizers) {
56+
ClientHttpRequestFactoryBuilder<?> builder = httpClientProperties.factoryBuilder();
57+
return customize(builder, clientHttpRequestFactoryBuilderCustomizers.orderedStream().toList());
58+
}
59+
60+
@SuppressWarnings("unchecked")
61+
private ClientHttpRequestFactoryBuilder<?> customize(ClientHttpRequestFactoryBuilder<?> builder,
62+
List<ClientHttpRequestFactoryBuilderCustomizer<?>> customizers) {
63+
ClientHttpRequestFactoryBuilder<?>[] builderReference = { builder };
64+
LambdaSafe.callbacks(ClientHttpRequestFactoryBuilderCustomizer.class, customizers, builderReference[0])
65+
.invoke((customizer) -> builderReference[0] = customizer.customize(builderReference[0]));
66+
return builderReference[0];
5367
}
5468

5569
@Bean

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/http/client/HttpClientAutoConfigurationTests.java

+32-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2024 the original author or authors.
2+
* Copyright 2012-2025 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.
@@ -27,9 +27,14 @@
2727
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
2828
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
2929
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings.Redirects;
30+
import org.springframework.boot.http.client.HttpComponentsClientHttpRequestFactoryBuilder;
31+
import org.springframework.boot.http.client.JettyClientHttpRequestFactoryBuilder;
3032
import org.springframework.boot.http.client.SimpleClientHttpRequestFactoryBuilder;
3133
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
3234
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
35+
import org.springframework.context.annotation.Bean;
36+
import org.springframework.context.annotation.Configuration;
37+
import org.springframework.http.client.ClientHttpRequestFactory;
3338

3439
import static org.assertj.core.api.Assertions.assertThat;
3540

@@ -89,4 +94,30 @@ void whenReactiveWebApplicationBeansAreNotConfigured() {
8994
.doesNotHaveBean(ClientHttpRequestFactorySettings.class));
9095
}
9196

97+
@Test
98+
void clientHttpRequestFactoryBuilderCustomizersAreApplied() {
99+
this.contextRunner.withUserConfiguration(ClientHttpRequestFactoryBuilderCustomizersConfiguration.class)
100+
.run((context) -> {
101+
ClientHttpRequestFactory factory = context.getBean(ClientHttpRequestFactoryBuilder.class).build();
102+
assertThat(factory).extracting("connectTimeout").isEqualTo(5L);
103+
});
104+
}
105+
106+
@Configuration(proxyBeanMethods = false)
107+
static class ClientHttpRequestFactoryBuilderCustomizersConfiguration {
108+
109+
@Bean
110+
ClientHttpRequestFactoryBuilderCustomizer<HttpComponentsClientHttpRequestFactoryBuilder> httpComponentsCustomizer() {
111+
return (builder) -> builder.withCustomizer((factory) -> factory.setConnectTimeout(5));
112+
}
113+
114+
@Bean
115+
ClientHttpRequestFactoryBuilderCustomizer<JettyClientHttpRequestFactoryBuilder> jettyCustomizer() {
116+
return (builder) -> {
117+
throw new IllegalStateException();
118+
};
119+
}
120+
121+
}
122+
92123
}

spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/io/rest-client.adoc

+1-1
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ spring:
272272
redirects: dont-follow
273273
----
274274

275-
For more complex customizations, you can declare your own javadoc:org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder[] bean which will cause auto-configuration to back off.
275+
For more complex customizations, you can use javadoc:org.springframework.boot.autoconfigure.http.client.ClientHttpRequestFactoryBuilderCustomizer[] or declare your own javadoc:org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder[] bean which will cause auto-configuration to back off.
276276
This can be useful when you need to customize some of the internals of the underlying HTTP library.
277277

278278
For example, the following will use a JDK client configured with a specific javadoc:java.net.ProxySelector[]:

0 commit comments

Comments
 (0)