Add ClientHttpRequestFactoryBuilderCustomizer support

Add `ClientHttpRequestFactoryBuilderCustomizer` interface that can be
used to customize the `ClientHttpRequestFactoryBuilder`.

Closes gh-44987
This commit is contained in:
Phillip Webb
2025-04-03 17:19:31 -07:00
parent 983e7b637b
commit 455bb0f835
4 changed files with 87 additions and 4 deletions

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2012-2025 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.http.client;
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
/**
* Customizer that can be used to modify the auto-configured
* {@link ClientHttpRequestFactoryBuilder} when its type matches.
*
* @param <B> the builder type
* @author Phillip Webb
* @since 3.5.0
*/
public interface ClientHttpRequestFactoryBuilderCustomizer<B extends ClientHttpRequestFactoryBuilder<?>> {
/**
* Customize the given builder.
* @param builder the builder to customize
* @return the customized builder
*/
B customize(B builder);
}

View File

@@ -16,6 +16,8 @@
package org.springframework.boot.autoconfigure.http.client;
import java.util.List;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@@ -29,6 +31,7 @@ import org.springframework.boot.http.client.ClientHttpRequestFactorySettings.Red
import org.springframework.boot.http.client.HttpClientSettings;
import org.springframework.boot.http.client.HttpRedirects;
import org.springframework.boot.ssl.SslBundles;
import org.springframework.boot.util.LambdaSafe;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.http.client.ClientHttpRequestFactory;
@@ -48,8 +51,19 @@ public class HttpClientAutoConfiguration {
@Bean
@ConditionalOnMissingBean
ClientHttpRequestFactoryBuilder<?> clientHttpRequestFactoryBuilder(HttpClientProperties httpClientProperties) {
return httpClientProperties.factoryBuilder();
ClientHttpRequestFactoryBuilder<?> clientHttpRequestFactoryBuilder(HttpClientProperties httpClientProperties,
ObjectProvider<ClientHttpRequestFactoryBuilderCustomizer<?>> clientHttpRequestFactoryBuilderCustomizers) {
ClientHttpRequestFactoryBuilder<?> builder = httpClientProperties.factoryBuilder();
return customize(builder, clientHttpRequestFactoryBuilderCustomizers.orderedStream().toList());
}
@SuppressWarnings("unchecked")
private ClientHttpRequestFactoryBuilder<?> customize(ClientHttpRequestFactoryBuilder<?> builder,
List<ClientHttpRequestFactoryBuilderCustomizer<?>> customizers) {
ClientHttpRequestFactoryBuilder<?>[] builderReference = { builder };
LambdaSafe.callbacks(ClientHttpRequestFactoryBuilderCustomizer.class, customizers, builderReference[0])
.invoke((customizer) -> builderReference[0] = customizer.customize(builderReference[0]));
return builderReference[0];
}
@Bean

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 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.
@@ -27,9 +27,14 @@ import org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration;
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings.Redirects;
import org.springframework.boot.http.client.HttpComponentsClientHttpRequestFactoryBuilder;
import org.springframework.boot.http.client.JettyClientHttpRequestFactoryBuilder;
import org.springframework.boot.http.client.SimpleClientHttpRequestFactoryBuilder;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import static org.assertj.core.api.Assertions.assertThat;
@@ -89,4 +94,30 @@ class HttpClientAutoConfigurationTests {
.doesNotHaveBean(ClientHttpRequestFactorySettings.class));
}
@Test
void clientHttpRequestFactoryBuilderCustomizersAreApplied() {
this.contextRunner.withUserConfiguration(ClientHttpRequestFactoryBuilderCustomizersConfiguration.class)
.run((context) -> {
ClientHttpRequestFactory factory = context.getBean(ClientHttpRequestFactoryBuilder.class).build();
assertThat(factory).extracting("connectTimeout").isEqualTo(5L);
});
}
@Configuration(proxyBeanMethods = false)
static class ClientHttpRequestFactoryBuilderCustomizersConfiguration {
@Bean
ClientHttpRequestFactoryBuilderCustomizer<HttpComponentsClientHttpRequestFactoryBuilder> httpComponentsCustomizer() {
return (builder) -> builder.withCustomizer((factory) -> factory.setConnectTimeout(5));
}
@Bean
ClientHttpRequestFactoryBuilderCustomizer<JettyClientHttpRequestFactoryBuilder> jettyCustomizer() {
return (builder) -> {
throw new IllegalStateException();
};
}
}
}

View File

@@ -272,7 +272,7 @@ spring:
redirects: dont-follow
----
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.
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.
This can be useful when you need to customize some of the internals of the underlying HTTP library.
For example, the following will use a JDK client configured with a specific javadoc:java.net.ProxySelector[]: