12

In Spring Boot 1.5.x, I was creating a custom RestTemplate like below:

@Bean
  public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
    PoolingHttpClientConnectionManager poolingConnectionManager = new PoolingHttpClientConnectionManager();
    poolingConnectionManager.setMaxTotal(restTemplateProps.getMaxConnectionsPerPool());
    poolingConnectionManager.setDefaultMaxPerRoute(restTemplateProps.getMaxDefaultConnectionPerRoute());
    CloseableHttpClient client = HttpClientBuilder.create().setConnectionManager(poolingConnectionManager).build();
    HttpComponentsClientHttpRequestFactory clientHttpRequestFactory =
        new HttpComponentsClientHttpRequestFactory(client);
    clientHttpRequestFactory.setConnectTimeout(restTemplateProps.getConnectionTimeout());
    clientHttpRequestFactory.setReadTimeout(restTemplateProps.getSocketTimeout());
    return restTemplateBuilder.requestFactory(clientHttpRequestFactory).build();
  }

But, after migrating to Spring Boot 2.1.x, the above code doesn't compile. Looks like requestFactory doesn't take HttpComponentsClientHttpRequestFactory as an input parameter.

Can anyone suggest how can I make the above code work in Spring Boot 2.1.x?

Sahil Chhabra
  • 10,621
  • 4
  • 63
  • 62

4 Answers4

17

After digging deeper into the source code of RestTemplateBuilder of Spring Boot 2.1.x, I found that they have removed the method requestFactory(ClientHttpRequestFactory requestFactory). That means you can no longer inject the ClientHttpRequestFactory object into requestFactory method.

But, it accept a Supplier<ClientHttpRequestFactory> as the input now. So if you have only one restTemplate and one requestFactory, all you need to do is register a HttpComponentsClientHttpRequestFactory bean in Spring context and pass a ClientHttpRequestFactorySupplier to requestFactory method. The supplier will automatically detect your HttpComponentsClientHttpRequestFactory and return you the required RestTemplate.

Refer the below code for that:

@Bean
  public HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory() {
    PoolingHttpClientConnectionManager poolingConnectionManager = new PoolingHttpClientConnectionManager();
    poolingConnectionManager.setMaxTotal(restTemplateProps.getMaxConnectionsPerPool());
    poolingConnectionManager.setDefaultMaxPerRoute(restTemplateProps.getMaxDefaultConnectionPerRoute());
    CloseableHttpClient client = HttpClientBuilder.create().setConnectionManager(poolingConnectionManager).build();
    HttpComponentsClientHttpRequestFactory clientHttpRequestFactory =
        new HttpComponentsClientHttpRequestFactory(client);
    clientHttpRequestFactory.setConnectTimeout(restTemplateProps.getConnectionTimeout());
    clientHttpRequestFactory.setReadTimeout(restTemplateProps.getSocketTimeout());
    return clientHttpRequestFactory;
  }

  @Bean
  public RestTemplate authRestTemplate(RestTemplateBuilder restTemplateBuilder) {
    return restTemplateBuilder.requestFactory(new ClientHttpRequestFactorySupplier()).build();
  }

For those who are interested in all the changes made to RestTemplateBuilder in SpringBoot 2.1.x, please refer this.

Tristan
  • 8,733
  • 7
  • 48
  • 96
Sahil Chhabra
  • 10,621
  • 4
  • 63
  • 62
  • 2
    Additional (and important) info: the [`ClientHttpRequestFactorySupplier()`](https://docs.spring.io/spring-boot/docs/2.1.0.M1/api/org/springframework/boot/web/client/ClientHttpRequestFactorySupplier.html) class exists only in Spring Boot *2.1.0* and above; thus, the solution applies to SpringBoot2.1.x+, and not to 2.x.x as stated. – gcpdev Jan 07 '19 at 11:03
  • ok but what if you have 2 restTemplate which need 2 distinct "requestFactory" ? How do you chose which requestFactory get injected into which restTemplate since it's auto detected by "ClientHttpRequestFactorySupplier()" ? – Tristan May 17 '19 at 14:13
  • I think you already got the answer, it can be done by implementing your own Supplier. – Sahil Chhabra May 23 '19 at 20:46
10

Or even easier, just:

 restTemplatebuilder.requestFactory(() -> new 
    HttpComponentsClientHttpRequestFactory(httpClient)).build();
Dariusz Urbanek
  • 166
  • 1
  • 11
  • Or even easier with Kotlin, just: `restTemplateBuilder .requestFactory { HttpComponentsClientHttpRequestFactory(httpClient)).build() } – slorinc Feb 28 '22 at 22:01
6

Here is a more general solution if you have multiple restTemplates which need different requestFactories (and not an auto-injection of the first one found) :

@Configuration
public class Config {

  @Bean
  public RestTemplate restTemplate(RestTemplateBuilder builder, ApplicationContext ctx) {
    return builder.requestFactory(new MyRequestFactorySupplier()).build();
  }

  class MyRequestFactorySupplier implements Supplier<ClientHttpRequestFactory> {

    @Override
    public ClientHttpRequestFactory get() {

      // Using Apache HTTP client.
      HttpClientBuilder clientBuilder = HttpClientBuilder.create();
      HttpClient httpClient = clientBuilder.build();
      HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
      requestFactory.setBufferRequestBody(false); // When sending large amounts of data via POST or PUT, it is recommended to change this property to false, so as not to run out of memory.
      return requestFactory;
    }

  }

}
Tristan
  • 8,733
  • 7
  • 48
  • 96
0

You can also just do this:

restTemplatebuilder.requestFactory(() -> { return new 
    HttpComponentsClientHttpRequestFactory(httpClient);});
tstiemer
  • 110
  • 8