Forked from jkuipers/HttpClientAutoConfiguration.java
Created
March 14, 2021 16:44
-
-
Save wycliffepeart/a29f897152940b0870be1af335799171 to your computer and use it in GitHub Desktop.
Spring Boot auto-configuration example for an Apache Components HTTP client and its usage in all RestTemplates created by the RestTemplateBuilder, plus trace logging support
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import org.apache.http.client.HttpClient; | |
import org.apache.http.client.config.CookieSpecs; | |
import org.apache.http.client.config.RequestConfig; | |
import org.apache.http.impl.client.CloseableHttpClient; | |
import org.apache.http.impl.client.HttpClients; | |
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | |
import org.springframework.boot.context.properties.EnableConfigurationProperties; | |
import org.springframework.boot.web.client.RestTemplateCustomizer; | |
import org.springframework.context.annotation.Bean; | |
import org.springframework.context.annotation.Configuration; | |
import org.springframework.http.client.BufferingClientHttpRequestFactory; | |
import org.springframework.http.client.ClientHttpRequestFactory; | |
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; | |
import org.springframework.http.client.InterceptingClientHttpRequestFactory; | |
import org.springframework.http.converter.HttpMessageConverter; | |
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; | |
import org.springframework.util.ReflectionUtils; | |
import org.springframework.web.client.RestTemplate; | |
import java.lang.reflect.Field; | |
/** | |
* Ensures that every {@link org.springframework.web.client.RestTemplate} built with a | |
* {@link org.springframework.boot.web.client.RestTemplateBuilder} uses an Apache Components | |
* HTTP client using a pooling connection pool with the configured number of maximum connections | |
* (default is way too small) and time-out settings. | |
*/ | |
@Configuration | |
@EnableConfigurationProperties(HttpClientProperties.class) | |
public class HttpClientAutoConfiguration { | |
@Autowired HttpClientProperties client; | |
@Bean | |
@ConditionalOnMissingBean | |
CloseableHttpClient httpClient(HttpClientConnectionManager connectionManager) { | |
RequestConfig requestConfig = RequestConfig.custom() | |
.setConnectTimeout(client.getConnectionTimeoutInMillis()) | |
.setSocketTimeout(client.getReadTimeoutInMillis()) | |
.setCookieSpec(CookieSpecs.STANDARD) | |
.build(); | |
return HttpClients.custom() | |
.setConnectionManager(connectionManager) | |
.setDefaultRequestConfig(requestConfig) | |
.build(); | |
} | |
@Bean(destroyMethod = "") // will be closed by httpClient | |
@ConditionalOnMissingBean | |
HttpClientConnectionManager httpClientConnectionManager() { | |
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(); | |
// since most services only connect to a single backend | |
// we use the same value for max per-route and total | |
connectionManager.setDefaultMaxPerRoute(client.getMaxConnections()); | |
connectionManager.setMaxTotal(client.getMaxConnections()); | |
return connectionManager; | |
} | |
@Bean | |
RestTemplateCustomizer httpClientCustomizer(HttpClient httpClient) { | |
return restTemplate -> { | |
ClientHttpRequestFactory requestFactory = restTemplate.getRequestFactory(); | |
// not-so-proudly copied from the TestRestTemplate code: | |
if (requestFactory instanceof InterceptingClientHttpRequestFactory) { | |
Field requestFactoryField = ReflectionUtils.findField(RestTemplate.class, "requestFactory"); | |
ReflectionUtils.makeAccessible(requestFactoryField); | |
requestFactory = (ClientHttpRequestFactory) ReflectionUtils.getField(requestFactoryField, restTemplate); | |
} | |
if (requestFactory instanceof HttpComponentsClientHttpRequestFactory) { | |
HttpComponentsClientHttpRequestFactory clientRequestFactory = | |
(HttpComponentsClientHttpRequestFactory) requestFactory; | |
clientRequestFactory.setHttpClient(httpClient); | |
if (client.isLoggingEnabled()) { | |
restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(requestFactory)); | |
restTemplate.getInterceptors().add(new LoggingClientHttpRequestInterceptor()); | |
} | |
} | |
}; | |
} | |
@Bean | |
RestTemplateCustomizer unwantedConvertersRemovingCustomizer() { | |
return restTemplate -> { | |
boolean foundFirstJsonConverter = false; | |
for (var iter = restTemplate.getMessageConverters().listIterator(); iter.hasNext(); ) { | |
HttpMessageConverter<?> converter = iter.next(); | |
if (converter instanceof MappingJackson2HttpMessageConverter) { | |
if (foundFirstJsonConverter) { | |
iter.remove(); | |
} else { | |
foundFirstJsonConverter = true; | |
} | |
} | |
} | |
}; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment