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
This commit is contained in:
Phillip Webb 2024-10-23 23:57:13 -07:00
parent 6356e904fc
commit 3a8b2e4bc8
21 changed files with 600 additions and 46 deletions

View File

@ -0,0 +1,70 @@
/*
* 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.
* 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.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.http.client.HttpClientProperties.Factory;
import org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
import org.springframework.boot.ssl.SslBundle;
import org.springframework.boot.ssl.SslBundles;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.util.StringUtils;
/**
* {@link EnableAutoConfiguration Auto-configuration} for
* {@link ClientHttpRequestFactoryBuilder} and {@link ClientHttpRequestFactorySettings}.
*
* @author Phillip Webb
* @since 3.4.0
*/
@AutoConfiguration(after = SslAutoConfiguration.class)
@ConditionalOnClass(ClientHttpRequestFactory.class)
@Conditional(NotReactiveWebApplicationCondition.class)
@EnableConfigurationProperties(HttpClientProperties.class)
public class HttpClientAutoConfiguration {
@Bean
@ConditionalOnMissingBean
ClientHttpRequestFactoryBuilder<?> clientHttpRequestFactoryBuilder(HttpClientProperties httpClientProperties) {
Factory factory = httpClientProperties.getFactory();
return (factory != null) ? factory.builder() : ClientHttpRequestFactoryBuilder.detect();
}
@Bean
@ConditionalOnMissingBean
ClientHttpRequestFactorySettings clientHttpRequestFactorySettings(HttpClientProperties httpClientProperties,
ObjectProvider<SslBundles> sslBundles) {
SslBundle sslBundle = getSslBundle(httpClientProperties.getSsl(), sslBundles);
return new ClientHttpRequestFactorySettings(httpClientProperties.getConnectTimeout(),
httpClientProperties.getReadTimeout(), sslBundle);
}
private SslBundle getSslBundle(HttpClientProperties.Ssl properties, ObjectProvider<SslBundles> sslBundles) {
String name = properties.getBundle();
return (StringUtils.hasLength(name)) ? sslBundles.getObject().getBundle(name) : null;
}
}

View File

@ -0,0 +1,145 @@
/*
* 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.
* 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 java.time.Duration;
import java.util.function.Supplier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
/**
* {@link ConfigurationProperties @ConfigurationProperties} for a Spring's blocking HTTP
* clients.
*
* @author Phillip Webb
* @since 3.4.0
*/
@ConfigurationProperties("spring.http.client")
public class HttpClientProperties {
/**
* Default factory used for a client HTTP request.
*/
private Factory factory;
/**
* Default connect timeout for a client HTTP request.
*/
private Duration connectTimeout;
/**
* Default read timeout for a client HTTP request.
*/
private Duration readTimeout;
/**
* Default SSL configuration for a client HTTP request.
*/
private Ssl ssl = new Ssl();
public Factory getFactory() {
return this.factory;
}
public void setFactory(Factory factory) {
this.factory = factory;
}
public Duration getConnectTimeout() {
return this.connectTimeout;
}
public void setConnectTimeout(Duration connectTimeout) {
this.connectTimeout = connectTimeout;
}
public Duration getReadTimeout() {
return this.readTimeout;
}
public void setReadTimeout(Duration readTimeout) {
this.readTimeout = readTimeout;
}
public Ssl getSsl() {
return this.ssl;
}
/**
* Supported factory types.
*/
public enum Factory {
/**
* Apache HttpComponents HttpClient.
*/
HTTP_COMPONENTS(ClientHttpRequestFactoryBuilder::httpComponents),
/**
* Jetty's HttpClient.
*/
JETTY(ClientHttpRequestFactoryBuilder::jetty),
/**
* Reactor-Netty.
*/
REACTOR(ClientHttpRequestFactoryBuilder::reactor),
/**
* Java's HttpClient.
*/
JDK(ClientHttpRequestFactoryBuilder::jdk),
/**
* Standard JDK facilities.
*/
SIMPLE(ClientHttpRequestFactoryBuilder::simple);
private final Supplier<ClientHttpRequestFactoryBuilder<?>> builderSupplier;
Factory(Supplier<ClientHttpRequestFactoryBuilder<?>> builderSupplier) {
this.builderSupplier = builderSupplier;
}
ClientHttpRequestFactoryBuilder<?> builder() {
return this.builderSupplier.get();
}
}
/**
* SSL configuration.
*/
public static class Ssl {
/**
* SSL bundle to use.
*/
private String bundle;
public String getBundle() {
return this.bundle;
}
public void setBundle(String bundle) {
this.bundle = bundle;
}
}
}

View File

@ -0,0 +1,40 @@
/*
* 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.
* 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.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.NoneNestedConditions;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
/**
* {@link SpringBootCondition} that applies only when running in a non-reactive web
* application.
*
* @author Phillip Webb
*/
class NotReactiveWebApplicationCondition extends NoneNestedConditions {
NotReactiveWebApplicationCondition() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
private static final class ReactiveWebApplication {
}
}

View File

@ -0,0 +1,20 @@
/*
* 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.
* 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.
*/
/**
* Auto-configuration for client-side HTTP.
*/
package org.springframework.boot.autoconfigure.http.client;

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -18,10 +18,10 @@ package org.springframework.boot.autoconfigure.web.client;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
import org.springframework.boot.ssl.SslBundle; import org.springframework.boot.ssl.SslBundle;
import org.springframework.boot.ssl.SslBundles; import org.springframework.boot.ssl.SslBundles;
import org.springframework.boot.web.client.ClientHttpRequestFactories;
import org.springframework.boot.web.client.ClientHttpRequestFactorySettings;
import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.web.client.RestClient; import org.springframework.web.client.RestClient;
@ -32,9 +32,13 @@ import org.springframework.web.client.RestClient;
*/ */
class AutoConfiguredRestClientSsl implements RestClientSsl { class AutoConfiguredRestClientSsl implements RestClientSsl {
private final ClientHttpRequestFactoryBuilder<?> clientHttpRequestFactoryBuilder;
private final SslBundles sslBundles; private final SslBundles sslBundles;
AutoConfiguredRestClientSsl(SslBundles sslBundles) { AutoConfiguredRestClientSsl(ClientHttpRequestFactoryBuilder<?> clientHttpRequestFactoryBuilder,
SslBundles sslBundles) {
this.clientHttpRequestFactoryBuilder = clientHttpRequestFactoryBuilder;
this.sslBundles = sslBundles; this.sslBundles = sslBundles;
} }
@ -46,8 +50,8 @@ class AutoConfiguredRestClientSsl implements RestClientSsl {
@Override @Override
public Consumer<RestClient.Builder> fromBundle(SslBundle bundle) { public Consumer<RestClient.Builder> fromBundle(SslBundle bundle) {
return (builder) -> { return (builder) -> {
ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.DEFAULTS.withSslBundle(bundle); ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.ofSslBundle(bundle);
ClientHttpRequestFactory requestFactory = ClientHttpRequestFactories.get(settings); ClientHttpRequestFactory requestFactory = this.clientHttpRequestFactoryBuilder.build(settings);
builder.requestFactory(requestFactory); builder.requestFactory(requestFactory);
}; };
} }

View File

@ -24,10 +24,11 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters; import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration; import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.http.client.HttpClientAutoConfiguration;
import org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration; 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.ssl.SslBundles; import org.springframework.boot.ssl.SslBundles;
import org.springframework.boot.web.client.ClientHttpRequestFactories;
import org.springframework.boot.web.client.ClientHttpRequestFactorySettings;
import org.springframework.boot.web.client.RestClientCustomizer; import org.springframework.boot.web.client.RestClientCustomizer;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Conditional;
@ -48,7 +49,8 @@ import org.springframework.web.client.RestClient.Builder;
* @author Moritz Halbritter * @author Moritz Halbritter
* @since 3.2.0 * @since 3.2.0
*/ */
@AutoConfiguration(after = { HttpMessageConvertersAutoConfiguration.class, SslAutoConfiguration.class }) @AutoConfiguration(after = { HttpClientAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class,
SslAutoConfiguration.class })
@ConditionalOnClass(RestClient.class) @ConditionalOnClass(RestClient.class)
@Conditional(NotReactiveWebApplicationCondition.class) @Conditional(NotReactiveWebApplicationCondition.class)
public class RestClientAutoConfiguration { public class RestClientAutoConfiguration {
@ -64,14 +66,21 @@ public class RestClientAutoConfiguration {
@Bean @Bean
@ConditionalOnMissingBean(RestClientSsl.class) @ConditionalOnMissingBean(RestClientSsl.class)
@ConditionalOnBean(SslBundles.class) @ConditionalOnBean(SslBundles.class)
AutoConfiguredRestClientSsl restClientSsl(SslBundles sslBundles) { AutoConfiguredRestClientSsl restClientSsl(
return new AutoConfiguredRestClientSsl(sslBundles); ObjectProvider<ClientHttpRequestFactoryBuilder<?>> clientHttpRequestFactoryBuilder, SslBundles sslBundles) {
return new AutoConfiguredRestClientSsl(
clientHttpRequestFactoryBuilder.getIfAvailable(ClientHttpRequestFactoryBuilder::detect), sslBundles);
} }
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
RestClientBuilderConfigurer restClientBuilderConfigurer(ObjectProvider<RestClientCustomizer> customizerProvider) { RestClientBuilderConfigurer restClientBuilderConfigurer(
ObjectProvider<ClientHttpRequestFactoryBuilder<?>> clientHttpRequestFactoryBuilder,
ObjectProvider<ClientHttpRequestFactorySettings> clientHttpRequestFactorySettings,
ObjectProvider<RestClientCustomizer> customizerProvider) {
RestClientBuilderConfigurer configurer = new RestClientBuilderConfigurer(); RestClientBuilderConfigurer configurer = new RestClientBuilderConfigurer();
configurer.setRequestFactoryBuilder(clientHttpRequestFactoryBuilder.getIfAvailable());
configurer.setRequestFactorySettings(clientHttpRequestFactorySettings.getIfAvailable());
configurer.setRestClientCustomizers(customizerProvider.orderedStream().toList()); configurer.setRestClientCustomizers(customizerProvider.orderedStream().toList());
return configurer; return configurer;
} }
@ -80,9 +89,7 @@ public class RestClientAutoConfiguration {
@Scope("prototype") @Scope("prototype")
@ConditionalOnMissingBean @ConditionalOnMissingBean
RestClient.Builder restClientBuilder(RestClientBuilderConfigurer restClientBuilderConfigurer) { RestClient.Builder restClientBuilder(RestClientBuilderConfigurer restClientBuilderConfigurer) {
RestClient.Builder builder = RestClient.builder() return restClientBuilderConfigurer.configure(RestClient.builder());
.requestFactory(ClientHttpRequestFactories.get(ClientHttpRequestFactorySettings.DEFAULTS));
return restClientBuilderConfigurer.configure(builder);
} }
} }

View File

@ -18,6 +18,8 @@ package org.springframework.boot.autoconfigure.web.client;
import java.util.List; import java.util.List;
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
import org.springframework.boot.web.client.RestClientCustomizer; import org.springframework.boot.web.client.RestClientCustomizer;
import org.springframework.web.client.RestClient; import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestClient.Builder; import org.springframework.web.client.RestClient.Builder;
@ -30,8 +32,20 @@ import org.springframework.web.client.RestClient.Builder;
*/ */
public class RestClientBuilderConfigurer { public class RestClientBuilderConfigurer {
private ClientHttpRequestFactoryBuilder<?> requestFactoryBuilder;
private ClientHttpRequestFactorySettings requestFactorySettings;
private List<RestClientCustomizer> customizers; private List<RestClientCustomizer> customizers;
void setRequestFactoryBuilder(ClientHttpRequestFactoryBuilder<?> requestFactoryBuilder) {
this.requestFactoryBuilder = requestFactoryBuilder;
}
void setRequestFactorySettings(ClientHttpRequestFactorySettings requestFactorySettings) {
this.requestFactorySettings = requestFactorySettings;
}
void setRestClientCustomizers(List<RestClientCustomizer> customizers) { void setRestClientCustomizers(List<RestClientCustomizer> customizers) {
this.customizers = customizers; this.customizers = customizers;
} }
@ -43,6 +57,9 @@ public class RestClientBuilderConfigurer {
* @return the configured builder * @return the configured builder
*/ */
public RestClient.Builder configure(RestClient.Builder builder) { public RestClient.Builder configure(RestClient.Builder builder) {
ClientHttpRequestFactoryBuilder<?> requestFactoryBuilder = (this.requestFactoryBuilder != null)
? this.requestFactoryBuilder : ClientHttpRequestFactoryBuilder.detect();
builder.requestFactory(requestFactoryBuilder.build(this.requestFactorySettings));
applyCustomizers(builder); applyCustomizers(builder);
return builder; return builder;
} }

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -18,10 +18,9 @@ package org.springframework.boot.autoconfigure.web.client;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
import org.springframework.boot.ssl.NoSuchSslBundleException; import org.springframework.boot.ssl.NoSuchSslBundleException;
import org.springframework.boot.ssl.SslBundle; import org.springframework.boot.ssl.SslBundle;
import org.springframework.boot.web.client.ClientHttpRequestFactories;
import org.springframework.boot.web.client.ClientHttpRequestFactorySettings;
import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.web.client.RestClient; import org.springframework.web.client.RestClient;
@ -38,8 +37,7 @@ import org.springframework.web.client.RestClient;
* </pre> NOTE: Apply SSL configuration will replace any previously * </pre> NOTE: Apply SSL configuration will replace any previously
* {@link RestClient.Builder#requestFactory configured} {@link ClientHttpRequestFactory}. * {@link RestClient.Builder#requestFactory configured} {@link ClientHttpRequestFactory}.
* If you need to configure {@link ClientHttpRequestFactory} with more than just SSL * If you need to configure {@link ClientHttpRequestFactory} with more than just SSL
* consider using a {@link ClientHttpRequestFactorySettings} with * consider using a {@link ClientHttpRequestFactoryBuilder}.
* {@link ClientHttpRequestFactories}.
* *
* @author Phillip Webb * @author Phillip Webb
* @since 3.2.0 * @since 3.2.0

View File

@ -23,6 +23,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters; import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration; import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.http.client.HttpClientAutoConfiguration;
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.boot.web.client.RestTemplateCustomizer; import org.springframework.boot.web.client.RestTemplateCustomizer;
import org.springframework.boot.web.client.RestTemplateRequestCustomizer; import org.springframework.boot.web.client.RestTemplateRequestCustomizer;
@ -32,13 +35,14 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
/** /**
* {@link EnableAutoConfiguration Auto-configuration} for {@link RestTemplate}. * {@link EnableAutoConfiguration Auto-configuration} for {@link RestTemplate} (via
* {@link RestTemplateBuilder}).
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Phillip Webb * @author Phillip Webb
* @since 1.4.0 * @since 1.4.0
*/ */
@AutoConfiguration(after = HttpMessageConvertersAutoConfiguration.class) @AutoConfiguration(after = { HttpClientAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class })
@ConditionalOnClass(RestTemplate.class) @ConditionalOnClass(RestTemplate.class)
@Conditional(NotReactiveWebApplicationCondition.class) @Conditional(NotReactiveWebApplicationCondition.class)
public class RestTemplateAutoConfiguration { public class RestTemplateAutoConfiguration {
@ -46,10 +50,14 @@ public class RestTemplateAutoConfiguration {
@Bean @Bean
@Lazy @Lazy
public RestTemplateBuilderConfigurer restTemplateBuilderConfigurer( public RestTemplateBuilderConfigurer restTemplateBuilderConfigurer(
ObjectProvider<ClientHttpRequestFactoryBuilder<?>> clientHttpRequestFactoryBuilder,
ObjectProvider<ClientHttpRequestFactorySettings> clientHttpRequestFactorySettings,
ObjectProvider<HttpMessageConverters> messageConverters, ObjectProvider<HttpMessageConverters> messageConverters,
ObjectProvider<RestTemplateCustomizer> restTemplateCustomizers, ObjectProvider<RestTemplateCustomizer> restTemplateCustomizers,
ObjectProvider<RestTemplateRequestCustomizer<?>> restTemplateRequestCustomizers) { ObjectProvider<RestTemplateRequestCustomizer<?>> restTemplateRequestCustomizers) {
RestTemplateBuilderConfigurer configurer = new RestTemplateBuilderConfigurer(); RestTemplateBuilderConfigurer configurer = new RestTemplateBuilderConfigurer();
configurer.setRequestFactoryBuilder(clientHttpRequestFactoryBuilder.getIfAvailable());
configurer.setRequestFactorySettings(clientHttpRequestFactorySettings.getIfAvailable());
configurer.setHttpMessageConverters(messageConverters.getIfUnique()); configurer.setHttpMessageConverters(messageConverters.getIfUnique());
configurer.setRestTemplateCustomizers(restTemplateCustomizers.orderedStream().toList()); configurer.setRestTemplateCustomizers(restTemplateCustomizers.orderedStream().toList());
configurer.setRestTemplateRequestCustomizers(restTemplateRequestCustomizers.orderedStream().toList()); configurer.setRestTemplateRequestCustomizers(restTemplateRequestCustomizers.orderedStream().toList());
@ -60,8 +68,7 @@ public class RestTemplateAutoConfiguration {
@Lazy @Lazy
@ConditionalOnMissingBean @ConditionalOnMissingBean
public RestTemplateBuilder restTemplateBuilder(RestTemplateBuilderConfigurer restTemplateBuilderConfigurer) { public RestTemplateBuilder restTemplateBuilder(RestTemplateBuilderConfigurer restTemplateBuilderConfigurer) {
RestTemplateBuilder builder = new RestTemplateBuilder(); return restTemplateBuilderConfigurer.configure(new RestTemplateBuilder());
return restTemplateBuilderConfigurer.configure(builder);
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2024 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -21,6 +21,8 @@ import java.util.List;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters; import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.boot.web.client.RestTemplateCustomizer; import org.springframework.boot.web.client.RestTemplateCustomizer;
import org.springframework.boot.web.client.RestTemplateRequestCustomizer; import org.springframework.boot.web.client.RestTemplateRequestCustomizer;
@ -34,12 +36,24 @@ import org.springframework.util.ObjectUtils;
*/ */
public final class RestTemplateBuilderConfigurer { public final class RestTemplateBuilderConfigurer {
private ClientHttpRequestFactoryBuilder<?> requestFactoryBuilder;
private ClientHttpRequestFactorySettings requestFactorySettings;
private HttpMessageConverters httpMessageConverters; private HttpMessageConverters httpMessageConverters;
private List<RestTemplateCustomizer> restTemplateCustomizers; private List<RestTemplateCustomizer> restTemplateCustomizers;
private List<RestTemplateRequestCustomizer<?>> restTemplateRequestCustomizers; private List<RestTemplateRequestCustomizer<?>> restTemplateRequestCustomizers;
void setRequestFactoryBuilder(ClientHttpRequestFactoryBuilder<?> requestFactoryBuilder) {
this.requestFactoryBuilder = requestFactoryBuilder;
}
void setRequestFactorySettings(ClientHttpRequestFactorySettings requestFactorySettings) {
this.requestFactorySettings = requestFactorySettings;
}
void setHttpMessageConverters(HttpMessageConverters httpMessageConverters) { void setHttpMessageConverters(HttpMessageConverters httpMessageConverters) {
this.httpMessageConverters = httpMessageConverters; this.httpMessageConverters = httpMessageConverters;
} }
@ -59,6 +73,12 @@ public final class RestTemplateBuilderConfigurer {
* @return the configured builder * @return the configured builder
*/ */
public RestTemplateBuilder configure(RestTemplateBuilder builder) { public RestTemplateBuilder configure(RestTemplateBuilder builder) {
if (this.requestFactoryBuilder != null) {
builder = builder.requestFactoryBuilder(this.requestFactoryBuilder);
}
if (this.requestFactorySettings != null) {
builder = builder.requestFactorySettings(this.requestFactorySettings);
}
if (this.httpMessageConverters != null) { if (this.httpMessageConverters != null) {
builder = builder.messageConverters(this.httpMessageConverters.getConverters()); builder = builder.messageConverters(this.httpMessageConverters.getConverters());
} }

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -23,6 +23,10 @@ import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.http.client.HttpClientAutoConfiguration;
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
import org.springframework.boot.webservices.client.WebServiceMessageSenderFactory;
import org.springframework.boot.webservices.client.WebServiceTemplateBuilder; import org.springframework.boot.webservices.client.WebServiceTemplateBuilder;
import org.springframework.boot.webservices.client.WebServiceTemplateCustomizer; import org.springframework.boot.webservices.client.WebServiceTemplateCustomizer;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@ -36,20 +40,35 @@ import org.springframework.ws.client.core.WebServiceTemplate;
* @author Dmytro Nosan * @author Dmytro Nosan
* @since 2.1.0 * @since 2.1.0
*/ */
@AutoConfiguration @AutoConfiguration(after = HttpClientAutoConfiguration.class)
@ConditionalOnClass({ WebServiceTemplate.class, Unmarshaller.class, Marshaller.class }) @ConditionalOnClass({ WebServiceTemplate.class, Unmarshaller.class, Marshaller.class })
public class WebServiceTemplateAutoConfiguration { public class WebServiceTemplateAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public WebServiceMessageSenderFactory webServiceHttpMessageSenderFactory(
ObjectProvider<ClientHttpRequestFactoryBuilder<?>> clientHttpRequestFactoryBuilder,
ObjectProvider<ClientHttpRequestFactorySettings> clientHttpRequestFactorySettings) {
return WebServiceMessageSenderFactory.http(
clientHttpRequestFactoryBuilder.getIfAvailable(ClientHttpRequestFactoryBuilder::detect),
clientHttpRequestFactorySettings.getIfAvailable());
}
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public WebServiceTemplateBuilder webServiceTemplateBuilder( public WebServiceTemplateBuilder webServiceTemplateBuilder(
ObjectProvider<WebServiceMessageSenderFactory> httpWebServiceMessageSenderBuilder,
ObjectProvider<WebServiceTemplateCustomizer> webServiceTemplateCustomizers) { ObjectProvider<WebServiceTemplateCustomizer> webServiceTemplateCustomizers) {
WebServiceTemplateBuilder builder = new WebServiceTemplateBuilder(); WebServiceTemplateBuilder templateBuilder = new WebServiceTemplateBuilder();
WebServiceMessageSenderFactory httpMessageSenderFactory = httpWebServiceMessageSenderBuilder.getIfAvailable();
if (httpMessageSenderFactory != null) {
templateBuilder = templateBuilder.httpMessageSenderFactory(httpMessageSenderFactory);
}
List<WebServiceTemplateCustomizer> customizers = webServiceTemplateCustomizers.orderedStream().toList(); List<WebServiceTemplateCustomizer> customizers = webServiceTemplateCustomizers.orderedStream().toList();
if (!customizers.isEmpty()) { if (!customizers.isEmpty()) {
builder = builder.customizers(customizers); templateBuilder = templateBuilder.customizers(customizers);
} }
return builder; return templateBuilder;
} }
} }

View File

@ -62,6 +62,7 @@ org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration
org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration
org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration
org.springframework.boot.autoconfigure.http.client.HttpClientAutoConfiguration
org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration

View File

@ -0,0 +1,90 @@
/*
* 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.
* 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 java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
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.SimpleClientHttpRequestFactoryBuilder;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link HttpClientAutoConfiguration}.
*
* @author Phillip Webb
*/
class HttpClientAutoConfigurationTests {
private static final AutoConfigurations autoConfigurations = AutoConfigurations
.of(HttpClientAutoConfiguration.class, SslAutoConfiguration.class);
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(autoConfigurations);
@Test
void configuresDetectedClientHttpRequestFactoryBuilder() {
this.contextRunner.run((context) -> assertThat(context).hasSingleBean(ClientHttpRequestFactoryBuilder.class));
}
@Test
void configuresDefinedClientHttpRequestFactoryBuilder() {
this.contextRunner.withPropertyValues("spring.http.client.factory=simple")
.run((context) -> assertThat(context.getBean(ClientHttpRequestFactoryBuilder.class))
.isInstanceOf(SimpleClientHttpRequestFactoryBuilder.class));
}
@Test
void configuresClientHttpRequestFactorySettings() {
this.contextRunner.withPropertyValues(sslPropertyValues().toArray(String[]::new))
.withPropertyValues("spring.http.client.connect-timeout=10s", "spring.http.client.read-timeout=20s",
"spring.http.client.ssl.bundle=test")
.run((context) -> {
ClientHttpRequestFactorySettings settings = context.getBean(ClientHttpRequestFactorySettings.class);
assertThat(settings.connectTimeout()).isEqualTo(Duration.ofSeconds(10));
assertThat(settings.readTimeout()).isEqualTo(Duration.ofSeconds(20));
assertThat(settings.sslBundle().getKey().getAlias()).isEqualTo("alias1");
});
}
private List<String> sslPropertyValues() {
List<String> propertyValues = new ArrayList<>();
String location = "classpath:org/springframework/boot/autoconfigure/ssl/";
propertyValues.add("spring.ssl.bundle.pem.test.key.alias=alias1");
propertyValues.add("spring.ssl.bundle.pem.test.truststore.type=PKCS12");
propertyValues.add("spring.ssl.bundle.pem.test.truststore.certificate=" + location + "rsa-cert.pem");
propertyValues.add("spring.ssl.bundle.pem.test.truststore.private-key=" + location + "rsa-key.pem");
return propertyValues;
}
@Test
void whenReactiveWebApplicationBeansAreNotConfigured() {
new ReactiveWebApplicationContextRunner().withConfiguration(autoConfigurations)
.run((context) -> assertThat(context).doesNotHaveBean(ClientHttpRequestFactoryBuilder.class)
.doesNotHaveBean(ClientHttpRequestFactorySettings.class));
}
}

View File

@ -0,0 +1,69 @@
/*
* 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.
* 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.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.http.client.HttpClientProperties.Factory;
import org.springframework.boot.http.client.HttpComponentsClientHttpRequestFactoryBuilder;
import org.springframework.boot.http.client.JdkClientHttpRequestFactoryBuilder;
import org.springframework.boot.http.client.JettyClientHttpRequestFactoryBuilder;
import org.springframework.boot.http.client.ReactorClientHttpRequestFactoryBuilder;
import org.springframework.boot.http.client.SimpleClientHttpRequestFactoryBuilder;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link HttpClientProperties}.
*
* @author Phillip Webb
*/
class HttpClientPropertiesTests {
@Nested
class FactoryTests {
@Test
void httpComponentsBuilder() {
assertThat(Factory.HTTP_COMPONENTS.builder())
.isInstanceOf(HttpComponentsClientHttpRequestFactoryBuilder.class);
}
@Test
void jettyBuilder() {
assertThat(Factory.JETTY.builder()).isInstanceOf(JettyClientHttpRequestFactoryBuilder.class);
}
@Test
void reactorBuilder() {
assertThat(Factory.REACTOR.builder()).isInstanceOf(ReactorClientHttpRequestFactoryBuilder.class);
}
@Test
void jdkBuilder() {
assertThat(Factory.JDK.builder()).isInstanceOf(JdkClientHttpRequestFactoryBuilder.class);
}
@Test
void simpleBuilder() {
assertThat(Factory.SIMPLE.builder()).isInstanceOf(SimpleClientHttpRequestFactoryBuilder.class);
}
}
}

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -23,12 +23,14 @@ import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters; import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration; import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.http.client.HttpClientAutoConfiguration;
import org.springframework.boot.ssl.SslBundles; import org.springframework.boot.ssl.SslBundles;
import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.web.client.RestClientCustomizer; import org.springframework.boot.web.client.RestClientCustomizer;
import org.springframework.boot.web.codec.CodecCustomizer; import org.springframework.boot.web.codec.CodecCustomizer;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
@ -49,7 +51,7 @@ import static org.mockito.Mockito.mock;
class RestClientAutoConfigurationTests { class RestClientAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(RestClientAutoConfiguration.class)); .withConfiguration(AutoConfigurations.of(RestClientAutoConfiguration.class, HttpClientAutoConfiguration.class));
@Test @Test
void shouldSupplyBeans() { void shouldSupplyBeans() {
@ -156,6 +158,19 @@ class RestClientAutoConfigurationTests {
}); });
} }
@Test
void whenHasFactoryProperty() {
this.contextRunner.withConfiguration(AutoConfigurations.of(HttpMessageConvertersAutoConfiguration.class))
.withUserConfiguration(RestClientConfig.class)
.withPropertyValues("spring.http.client.factory=simple")
.run((context) -> {
assertThat(context).hasSingleBean(RestClient.class);
RestClient restClient = context.getBean(RestClient.class);
assertThat(restClient).extracting("clientRequestFactory")
.isInstanceOf(SimpleClientHttpRequestFactory.class);
});
}
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
static class CodecConfiguration { static class CodecConfiguration {

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -25,6 +25,7 @@ import org.springframework.beans.factory.support.BeanDefinitionOverrideException
import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters; import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration; import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.http.client.HttpClientAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner; import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
@ -36,6 +37,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.mock.http.client.MockClientHttpRequest; import org.springframework.mock.http.client.MockClientHttpRequest;
@ -56,8 +58,8 @@ import static org.mockito.Mockito.mock;
*/ */
class RestTemplateAutoConfigurationTests { class RestTemplateAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration(
.withConfiguration(AutoConfigurations.of(RestTemplateAutoConfiguration.class)); AutoConfigurations.of(RestTemplateAutoConfiguration.class, HttpClientAutoConfiguration.class));
@Test @Test
void restTemplateBuilderConfigurerShouldBeLazilyDefined() { void restTemplateBuilderConfigurerShouldBeLazilyDefined() {
@ -191,6 +193,18 @@ class RestTemplateAutoConfigurationTests {
.doesNotHaveBean(RestTemplateBuilderConfigurer.class)); .doesNotHaveBean(RestTemplateBuilderConfigurer.class));
} }
@Test
void whenHasFactoryProperty() {
this.contextRunner.withConfiguration(AutoConfigurations.of(HttpMessageConvertersAutoConfiguration.class))
.withUserConfiguration(RestTemplateConfig.class)
.withPropertyValues("spring.http.client.factory=simple")
.run((context) -> {
assertThat(context).hasSingleBean(RestTemplate.class);
RestTemplate restTemplate = context.getBean(RestTemplate.class);
assertThat(restTemplate.getRequestFactory()).isInstanceOf(SimpleClientHttpRequestFactory.class);
});
}
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
static class RestTemplateConfig { static class RestTemplateConfig {

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -21,6 +21,8 @@ import java.util.function.Consumer;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.http.client.HttpClientAutoConfiguration;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext; import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.test.context.runner.ContextConsumer; import org.springframework.boot.test.context.runner.ContextConsumer;
@ -28,6 +30,7 @@ import org.springframework.boot.webservices.client.WebServiceTemplateBuilder;
import org.springframework.boot.webservices.client.WebServiceTemplateCustomizer; import org.springframework.boot.webservices.client.WebServiceTemplateCustomizer;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.oxm.Marshaller; import org.springframework.oxm.Marshaller;
import org.springframework.oxm.Unmarshaller; import org.springframework.oxm.Unmarshaller;
import org.springframework.oxm.jaxb.Jaxb2Marshaller; import org.springframework.oxm.jaxb.Jaxb2Marshaller;
@ -45,8 +48,8 @@ import static org.assertj.core.api.Assertions.assertThat;
*/ */
class WebServiceTemplateAutoConfigurationTests { class WebServiceTemplateAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration(
.withConfiguration(AutoConfigurations.of(WebServiceTemplateAutoConfiguration.class)); AutoConfigurations.of(WebServiceTemplateAutoConfiguration.class, HttpClientAutoConfiguration.class));
@Test @Test
void autoConfiguredBuilderShouldNotHaveMarshallerAndUnmarshaller() { void autoConfiguredBuilderShouldNotHaveMarshallerAndUnmarshaller() {
@ -93,6 +96,19 @@ class WebServiceTemplateAutoConfigurationTests {
.run((context) -> assertThat(context).hasNotFailed()); .run((context) -> assertThat(context).hasNotFailed());
} }
@Test
void whenHasFactoryProperty() {
this.contextRunner.withConfiguration(AutoConfigurations.of(HttpMessageConvertersAutoConfiguration.class))
.withPropertyValues("spring.http.client.factory=simple")
.run(assertWebServiceTemplateBuilder((builder) -> {
WebServiceTemplate webServiceTemplate = builder.build();
assertThat(webServiceTemplate.getMessageSenders()).hasSize(1);
ClientHttpRequestMessageSender messageSender = (ClientHttpRequestMessageSender) webServiceTemplate
.getMessageSenders()[0];
assertThat(messageSender.getRequestFactory()).isInstanceOf(SimpleClientHttpRequestFactory.class);
}));
}
private ContextConsumer<AssertableApplicationContext> assertWebServiceTemplateBuilder( private ContextConsumer<AssertableApplicationContext> assertWebServiceTemplateBuilder(
Consumer<WebServiceTemplateBuilder> builder) { Consumer<WebServiceTemplateBuilder> builder) {
return (context) -> { return (context) -> {

View File

@ -36,7 +36,8 @@ import static org.assertj.core.api.Assertions.assertThat;
* *
* @author Dave Syer * @author Dave Syer
*/ */
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { "management.server.port:0" }) @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
properties = { "management.server.port:0", "spring.http.client.factory=simple" })
class SampleActuatorUiApplicationPortTests { class SampleActuatorUiApplicationPortTests {
@LocalServerPort @LocalServerPort

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -39,7 +39,8 @@ import static org.assertj.core.api.Assertions.assertThat;
* *
* @author Dave Syer * @author Dave Syer
*/ */
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { "server.error.include-message=always" }) @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
properties = { "server.error.include-message=always", "spring.http.client.factory=simple" })
class SampleActuatorUiApplicationTests { class SampleActuatorUiApplicationTests {
@Autowired @Autowired
@ -50,7 +51,7 @@ class SampleActuatorUiApplicationTests {
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_HTML)); headers.setAccept(Arrays.asList(MediaType.TEXT_HTML));
ResponseEntity<String> entity = this.restTemplate.withBasicAuth("user", getPassword()) ResponseEntity<String> entity = this.restTemplate.withBasicAuth("user", getPassword())
.exchange("/", HttpMethod.GET, new HttpEntity<Void>(headers), String.class); .exchange("/", HttpMethod.GET, new HttpEntity<>(headers), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("<title>Hello"); assertThat(entity.getBody()).contains("<title>Hello");
} }
@ -74,7 +75,7 @@ class SampleActuatorUiApplicationTests {
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_HTML)); headers.setAccept(Arrays.asList(MediaType.TEXT_HTML));
ResponseEntity<String> entity = this.restTemplate.withBasicAuth("user", getPassword()) ResponseEntity<String> entity = this.restTemplate.withBasicAuth("user", getPassword())
.exchange("/error", HttpMethod.GET, new HttpEntity<Void>(headers), String.class); .exchange("/error", HttpMethod.GET, new HttpEntity<>(headers), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
assertThat(entity.getBody()).contains("<html>") assertThat(entity.getBody()).contains("<html>")
.contains("<body>") .contains("<body>")

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -48,7 +48,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Madhura Bhave * @author Madhura Bhave
*/ */
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = "server.servlet.session.timeout:2") properties = { "server.servlet.session.timeout:2", "spring.http.client.factory=simple" })
class SampleSessionJdbcApplicationTests { class SampleSessionJdbcApplicationTests {
@Autowired @Autowired

View File

@ -43,7 +43,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Dave Syer * @author Dave Syer
* @author Scott Frederick * @author Scott Frederick
*/ */
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = "spring.http.client.factory=simple")
class SampleMethodSecurityApplicationTests { class SampleMethodSecurityApplicationTests {
@LocalServerPort @LocalServerPort