Allow RestTemplateBuilder to be further customized
Closes gh-23389
This commit is contained in:
parent
a7c411609e
commit
1631ae23f5
|
|
@ -16,9 +16,6 @@
|
|||
|
||||
package org.springframework.boot.autoconfigure.web.client;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
|
|
@ -57,26 +54,24 @@ public class RestTemplateAutoConfiguration {
|
|||
@Bean
|
||||
@Lazy
|
||||
@ConditionalOnMissingBean
|
||||
public RestTemplateBuilder restTemplateBuilder(ObjectProvider<HttpMessageConverters> messageConverters,
|
||||
public RestTemplateBuilderConfigurer restTemplateBuilderConfigurer(
|
||||
ObjectProvider<HttpMessageConverters> messageConverters,
|
||||
ObjectProvider<RestTemplateCustomizer> restTemplateCustomizers,
|
||||
ObjectProvider<RestTemplateRequestCustomizer<?>> restTemplateRequestCustomizers) {
|
||||
RestTemplateBuilder builder = new RestTemplateBuilder();
|
||||
HttpMessageConverters converters = messageConverters.getIfUnique();
|
||||
if (converters != null) {
|
||||
builder = builder.messageConverters(converters.getConverters());
|
||||
}
|
||||
builder = addCustomizers(builder, restTemplateCustomizers, RestTemplateBuilder::customizers);
|
||||
builder = addCustomizers(builder, restTemplateRequestCustomizers, RestTemplateBuilder::requestCustomizers);
|
||||
return builder;
|
||||
RestTemplateBuilderConfigurer configurer = new RestTemplateBuilderConfigurer();
|
||||
configurer.setHttpMessageConverters(messageConverters.getIfUnique());
|
||||
configurer.setRestTemplateCustomizers(restTemplateCustomizers.orderedStream().collect(Collectors.toList()));
|
||||
configurer.setRestTemplateRequestCustomizers(
|
||||
restTemplateRequestCustomizers.orderedStream().collect(Collectors.toList()));
|
||||
return configurer;
|
||||
}
|
||||
|
||||
private <T> RestTemplateBuilder addCustomizers(RestTemplateBuilder builder, ObjectProvider<T> objectProvider,
|
||||
BiFunction<RestTemplateBuilder, Collection<T>, RestTemplateBuilder> method) {
|
||||
List<T> customizers = objectProvider.orderedStream().collect(Collectors.toList());
|
||||
if (!customizers.isEmpty()) {
|
||||
return method.apply(builder, customizers);
|
||||
}
|
||||
return builder;
|
||||
@Bean
|
||||
@Lazy
|
||||
@ConditionalOnMissingBean
|
||||
public RestTemplateBuilder restTemplateBuilder(RestTemplateBuilderConfigurer restTemplateBuilderConfigurer) {
|
||||
RestTemplateBuilder builder = new RestTemplateBuilder();
|
||||
return restTemplateBuilderConfigurer.configure(builder);
|
||||
}
|
||||
|
||||
static class NotReactiveWebApplicationCondition extends NoneNestedConditions {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright 2012-2020 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.web.client;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.boot.web.client.RestTemplateCustomizer;
|
||||
import org.springframework.boot.web.client.RestTemplateRequestCustomizer;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Configure {@link RestTemplateBuilder} with sensible defaults.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.4.0
|
||||
*/
|
||||
public final class RestTemplateBuilderConfigurer {
|
||||
|
||||
private HttpMessageConverters httpMessageConverters;
|
||||
|
||||
private List<RestTemplateCustomizer> restTemplateCustomizers;
|
||||
|
||||
private List<RestTemplateRequestCustomizer<?>> restTemplateRequestCustomizers;
|
||||
|
||||
void setHttpMessageConverters(HttpMessageConverters httpMessageConverters) {
|
||||
this.httpMessageConverters = httpMessageConverters;
|
||||
}
|
||||
|
||||
void setRestTemplateCustomizers(List<RestTemplateCustomizer> restTemplateCustomizers) {
|
||||
this.restTemplateCustomizers = restTemplateCustomizers;
|
||||
}
|
||||
|
||||
void setRestTemplateRequestCustomizers(List<RestTemplateRequestCustomizer<?>> restTemplateRequestCustomizers) {
|
||||
this.restTemplateRequestCustomizers = restTemplateRequestCustomizers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the specified {@link RestTemplateBuilder}. The builder can be further
|
||||
* tuned and default settings can be overridden.
|
||||
* @param builder the {@link RestTemplateBuilder} instance to configure
|
||||
* @return the configured builder
|
||||
*/
|
||||
public RestTemplateBuilder configure(RestTemplateBuilder builder) {
|
||||
if (this.httpMessageConverters != null) {
|
||||
builder = builder.messageConverters(this.httpMessageConverters.getConverters());
|
||||
}
|
||||
builder = addCustomizers(builder, this.restTemplateCustomizers, RestTemplateBuilder::customizers);
|
||||
builder = addCustomizers(builder, this.restTemplateRequestCustomizers, RestTemplateBuilder::requestCustomizers);
|
||||
return builder;
|
||||
}
|
||||
|
||||
private <T> RestTemplateBuilder addCustomizers(RestTemplateBuilder builder, List<T> customizers,
|
||||
BiFunction<RestTemplateBuilder, Collection<T>, RestTemplateBuilder> method) {
|
||||
if (!ObjectUtils.isEmpty(customizers)) {
|
||||
return method.apply(builder, customizers);
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -46,6 +46,7 @@ import static org.mockito.ArgumentMatchers.any;
|
|||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
|
||||
/**
|
||||
* Tests for {@link RestTemplateAutoConfiguration}
|
||||
|
|
@ -58,6 +59,12 @@ class RestTemplateAutoConfigurationTests {
|
|||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(RestTemplateAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void restTemplateBuilderConfigurerShouldBeLazilyDefined() {
|
||||
this.contextRunner.run((context) -> assertThat(
|
||||
context.getBeanFactory().getBeanDefinition("restTemplateBuilderConfigurer").isLazyInit()).isTrue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void restTemplateBuilderShouldBeLazilyDefined() {
|
||||
this.contextRunner.run(
|
||||
|
|
@ -101,24 +108,40 @@ class RestTemplateAutoConfigurationTests {
|
|||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void restTemplateShouldApplyCustomizer() {
|
||||
this.contextRunner.withUserConfiguration(RestTemplateConfig.class, RestTemplateCustomizerConfig.class)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(RestTemplate.class);
|
||||
RestTemplate restTemplate = context.getBean(RestTemplate.class);
|
||||
RestTemplateCustomizer customizer = context.getBean(RestTemplateCustomizer.class);
|
||||
verify(customizer).customize(restTemplate);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void restTemplateWhenHasCustomBuilderShouldUseCustomBuilder() {
|
||||
this.contextRunner.withUserConfiguration(RestTemplateConfig.class, CustomRestTemplateBuilderConfig.class)
|
||||
this.contextRunner.withUserConfiguration(RestTemplateConfig.class, CustomRestTemplateBuilderConfig.class,
|
||||
RestTemplateCustomizerConfig.class).run((context) -> {
|
||||
assertThat(context).hasSingleBean(RestTemplate.class);
|
||||
RestTemplate restTemplate = context.getBean(RestTemplate.class);
|
||||
assertThat(restTemplate.getMessageConverters()).hasSize(1);
|
||||
assertThat(restTemplate.getMessageConverters().get(0))
|
||||
.isInstanceOf(CustomHttpMessageConverter.class);
|
||||
verifyNoInteractions(context.getBean(RestTemplateCustomizer.class));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void restTemplateWhenHasCustomBuilderCouldReuseBuilderConfigurer() {
|
||||
this.contextRunner.withUserConfiguration(RestTemplateConfig.class,
|
||||
CustomRestTemplateBuilderWithConfigurerConfig.class, RestTemplateCustomizerConfig.class)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(RestTemplate.class);
|
||||
RestTemplate restTemplate = context.getBean(RestTemplate.class);
|
||||
assertThat(restTemplate.getMessageConverters()).hasSize(1);
|
||||
assertThat(restTemplate.getMessageConverters().get(0))
|
||||
.isInstanceOf(CustomHttpMessageConverter.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void restTemplateShouldApplyCustomizer() {
|
||||
this.contextRunner.withUserConfiguration(RestTemplateConfig.class, RestTemplateCustomizerConfig.class)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(RestTemplate.class);
|
||||
RestTemplate restTemplate = context.getBean(RestTemplate.class);
|
||||
RestTemplateCustomizer customizer = context.getBean(RestTemplateCustomizer.class);
|
||||
verify(customizer).customize(restTemplate);
|
||||
});
|
||||
|
|
@ -147,14 +170,16 @@ class RestTemplateAutoConfigurationTests {
|
|||
@Test
|
||||
void whenServletWebApplicationRestTemplateBuilderIsConfigured() {
|
||||
new WebApplicationContextRunner().withConfiguration(AutoConfigurations.of(RestTemplateAutoConfiguration.class))
|
||||
.run((context) -> assertThat(context).hasSingleBean(RestTemplateBuilder.class));
|
||||
.run((context) -> assertThat(context).hasSingleBean(RestTemplateBuilder.class)
|
||||
.hasSingleBean(RestTemplateBuilderConfigurer.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenReactiveWebApplicationRestTemplateBuilderIsNotConfigured() {
|
||||
new ReactiveWebApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(RestTemplateAutoConfiguration.class))
|
||||
.run((context) -> assertThat(context).doesNotHaveBean(RestTemplateBuilder.class));
|
||||
.run((context) -> assertThat(context).doesNotHaveBean(RestTemplateBuilder.class)
|
||||
.doesNotHaveBean(RestTemplateBuilderConfigurer.class));
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
|
|
@ -208,6 +233,16 @@ class RestTemplateAutoConfigurationTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomRestTemplateBuilderWithConfigurerConfig {
|
||||
|
||||
@Bean
|
||||
RestTemplateBuilder restTemplateBuilder(RestTemplateBuilderConfigurer configurer) {
|
||||
return configurer.configure(new RestTemplateBuilder()).messageConverters(new CustomHttpMessageConverter());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class RestTemplateCustomizerConfig {
|
||||
|
||||
|
|
|
|||
|
|
@ -5975,7 +5975,16 @@ The following example shows a customizer that configures the use of a proxy for
|
|||
include::{code-examples}/web/client/RestTemplateProxyCustomizationExample.java[tag=customizer]
|
||||
----
|
||||
|
||||
Finally, the most extreme (and rarely used) option is to create your own `RestTemplateBuilder` bean.
|
||||
Finally, you can also create your own `RestTemplateBuilder` bean.
|
||||
To prevent switching off the auto-configuration of a `RestTemplateBuilder` and prevent any `RestTemplateCustomizer` beans from being used, make sure to configure your custom instance with a `RestTemplateBuilderConfigurer`.
|
||||
The following example exposes a `RestTemplateBuilder` with what Spring Boot would auto-configure, except that custom connect and read timeouts are also specified:
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::{code-examples}/web/client/RestTemplateBuilderCustomizationExample.java[tag=customizer]
|
||||
----
|
||||
|
||||
The most extreme (and rarely used) option is to create your own `RestTemplateBuilder` bean without using a configurer.
|
||||
Doing so switches off the auto-configuration of a `RestTemplateBuilder` and prevents any `RestTemplateCustomizer` beans from being used.
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright 2012-2020 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.docs.web.client;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.springframework.boot.autoconfigure.web.client.RestTemplateBuilderConfigurer;
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Example configuration for using a {@link RestTemplateBuilderConfigurer} to configure a
|
||||
* custom {@link RestTemplateBuilder}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class RestTemplateBuilderCustomizationExample {
|
||||
|
||||
// tag::customizer[]
|
||||
@Bean
|
||||
public RestTemplateBuilder restTemplateBuilder(RestTemplateBuilderConfigurer configurer) {
|
||||
return configurer.configure(new RestTemplateBuilder()).setConnectTimeout(Duration.ofSeconds(5))
|
||||
.setReadTimeout(Duration.ofSeconds(2));
|
||||
}
|
||||
// end::customizer[]
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue