Auto-configure HTTP client builders as Lazy
Prior to this commit, HTTP client builders auto-configured by Spring Boot would be eagerly instantiating resources, even if those were not used by the application. This commit makes the `RestTemplateBuilder` bean as Lazy. `WebClient.Builder` was already a prototype bean, but some of its dependencies could consume resources, like the `HttpClientConnector` and the related infrastructure. This commit makes those pieces lazy. Note that since those components are meant to help instantiate actual HTTP clients for application components, making them lazy won't make any difference at runtime since they'll be used during context refresh, or they won't be used at all. Closes gh-19549
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* 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.
|
||||
@@ -38,6 +38,7 @@ import org.springframework.boot.web.client.RestTemplateRequestCustomizer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
@@ -54,6 +55,7 @@ import org.springframework.web.client.RestTemplate;
|
||||
public class RestTemplateAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@Lazy
|
||||
@ConditionalOnMissingBean
|
||||
public RestTemplateBuilder restTemplateBuilder(ObjectProvider<HttpMessageConverters> messageConverters,
|
||||
ObjectProvider<RestTemplateCustomizer> restTemplateCustomizers,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* 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.
|
||||
@@ -23,6 +23,7 @@ import org.springframework.boot.web.reactive.function.client.WebClientCustomizer
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.client.reactive.ClientHttpConnector;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
@@ -43,6 +44,7 @@ import org.springframework.web.reactive.function.client.WebClient;
|
||||
public class ClientHttpConnectorAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@Lazy
|
||||
@Order(0)
|
||||
@ConditionalOnBean(ClientHttpConnector.class)
|
||||
public WebClientCustomizer clientConnectorCustomizer(ClientHttpConnector clientHttpConnector) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* 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.
|
||||
@@ -25,6 +25,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.http.client.reactive.ClientHttpConnector;
|
||||
import org.springframework.http.client.reactive.JettyClientHttpConnector;
|
||||
import org.springframework.http.client.reactive.JettyResourceFactory;
|
||||
@@ -54,6 +55,7 @@ class ClientHttpConnectorConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Lazy
|
||||
public ReactorClientHttpConnector reactorClientHttpConnector(ReactorResourceFactory reactorResourceFactory) {
|
||||
return new ReactorClientHttpConnector(reactorResourceFactory, Function.identity());
|
||||
}
|
||||
@@ -72,6 +74,7 @@ class ClientHttpConnectorConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Lazy
|
||||
public JettyClientHttpConnector jettyClientHttpConnector(JettyResourceFactory jettyResourceFactory) {
|
||||
SslContextFactory sslContextFactory = new SslContextFactory.Client();
|
||||
HttpClient httpClient = new HttpClient(sslContextFactory);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* 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.
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package org.springframework.boot.autoconfigure.web.reactive.function.client;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
@@ -49,18 +49,13 @@ import org.springframework.web.reactive.function.client.WebClient;
|
||||
@AutoConfigureAfter({ CodecsAutoConfiguration.class, ClientHttpConnectorAutoConfiguration.class })
|
||||
public class WebClientAutoConfiguration {
|
||||
|
||||
private final WebClient.Builder webClientBuilder;
|
||||
|
||||
public WebClientAutoConfiguration(ObjectProvider<WebClientCustomizer> customizerProvider) {
|
||||
this.webClientBuilder = WebClient.builder();
|
||||
customizerProvider.orderedStream().forEach((customizer) -> customizer.customize(this.webClientBuilder));
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Scope("prototype")
|
||||
@ConditionalOnMissingBean
|
||||
public WebClient.Builder webClientBuilder() {
|
||||
return this.webClientBuilder.clone();
|
||||
public WebClient.Builder webClientBuilder(ObjectProvider<WebClientCustomizer> customizerProvider) {
|
||||
WebClient.Builder builder = WebClient.builder();
|
||||
customizerProvider.orderedStream().forEach((customizer) -> customizer.customize(builder));
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@@ -70,8 +65,8 @@ public class WebClientAutoConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@Order(0)
|
||||
public WebClientCodecCustomizer exchangeStrategiesCustomizer(List<CodecCustomizer> codecCustomizers) {
|
||||
return new WebClientCodecCustomizer(codecCustomizers);
|
||||
public WebClientCodecCustomizer exchangeStrategiesCustomizer(ObjectProvider<CodecCustomizer> codecCustomizers) {
|
||||
return new WebClientCodecCustomizer(codecCustomizers.orderedStream().collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* 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.
|
||||
@@ -20,7 +20,6 @@ import java.util.List;
|
||||
|
||||
import org.springframework.boot.web.codec.CodecCustomizer;
|
||||
import org.springframework.boot.web.reactive.function.client.WebClientCustomizer;
|
||||
import org.springframework.web.reactive.function.client.ExchangeStrategies;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
@@ -39,9 +38,8 @@ public class WebClientCodecCustomizer implements WebClientCustomizer {
|
||||
|
||||
@Override
|
||||
public void customize(WebClient.Builder webClientBuilder) {
|
||||
webClientBuilder.exchangeStrategies(ExchangeStrategies.builder()
|
||||
.codecs((codecs) -> this.codecCustomizers.forEach((customizer) -> customizer.customize(codecs)))
|
||||
.build());
|
||||
webClientBuilder
|
||||
.codecs((codecs) -> this.codecCustomizers.forEach((customizer) -> customizer.customize(codecs)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* 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.
|
||||
@@ -58,6 +58,13 @@ class RestTemplateAutoConfigurationTests {
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(RestTemplateAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void restTemplateBuilderShouldBeLazilyDefined() {
|
||||
this.contextRunner.run(
|
||||
(context) -> assertThat(context.getBeanFactory().getBeanDefinition("restTemplateBuilder").isLazyInit())
|
||||
.isTrue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void restTemplateWhenMessageConvertersDefinedShouldHaveMessageConverters() {
|
||||
this.contextRunner.withConfiguration(AutoConfigurations.of(HttpMessageConvertersAutoConfiguration.class))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* 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.
|
||||
@@ -18,6 +18,7 @@ package org.springframework.boot.autoconfigure.web.reactive.function.client;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.boot.web.reactive.function.client.WebClientCustomizer;
|
||||
@@ -44,6 +45,18 @@ class ClientHttpConnectorAutoConfigurationTests {
|
||||
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(ClientHttpConnectorAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void shouldCreateResourcesLazily() {
|
||||
this.contextRunner.run((context) -> {
|
||||
BeanDefinition customizerDefinition = context.getBeanFactory()
|
||||
.getBeanDefinition("clientConnectorCustomizer");
|
||||
assertThat(customizerDefinition.isLazyInit()).isTrue();
|
||||
BeanDefinition connectorDefinition = context.getBeanFactory()
|
||||
.getBeanDefinition("reactorClientHttpConnector");
|
||||
assertThat(connectorDefinition.isLazyInit()).isTrue();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateHttpClientBeans() {
|
||||
this.contextRunner.run((context) -> {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* 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.
|
||||
@@ -102,7 +102,7 @@ class WebClientAutoConfigurationTests {
|
||||
verify(secondConnector).connect(eq(HttpMethod.GET), eq(URI.create("https://second.example.org/foo")),
|
||||
any());
|
||||
WebClientCustomizer customizer = context.getBean("webClientCustomizer", WebClientCustomizer.class);
|
||||
verify(customizer, times(1)).customize(any(WebClient.Builder.class));
|
||||
verify(customizer, times(2)).customize(any(WebClient.Builder.class));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user