application.yml:

asimiotech:
  rest-templates:
    -
      bean-id: restTemplate1
      max-total: 20
      default-max-per-route: 20
      conn-timeout-millis: 4000
      conn-request-timeout-millis: 4000
      socket-timeout-millis: 4000
      routes:
        -
          scheme: http
          host: localhost
          port: 8000
          max-per-route: 20
    -
      bean-id: restTemplate2
      max-total: 10
      default-max-per-route: 10
      conn-timeout-millis: 2000
      conn-request-timeout-millis: 2000
      socket-timeout-millis: 2000
      routes:
        -
          scheme: http
          host: localhost
          port: 8080
          max-per-route: 10

AsimioTechProperties.java:

@Configuration
@ConfigurationProperties(prefix = "asimiotech")
@Getter
@Setter
public class AsimioTechProperties {

  private List<RestTemplateProperties> restTemplates;

  @Getter
  @Setter
  public static class RestTemplateProperties {

    private String beanId;
    private Integer maxTotal;
    private Integer defaultMaxPerRoute;
    private Integer connTimeoutMillis;
    private Integer connRequestTimeoutMillis;
    private Integer socketTimeoutMillis;
    private List<RouteProperties> routes;

  }

  @Getter
  @Setter
  public static class RouteProperties {

    private String scheme;
    private String host;
    private Integer port;
    private Integer maxPerRoute;

  }

}

RestTemplateConfig.java:

@Configuration
@EnableConfigurationProperties(AsimioTechProperties.class)
@RequiredArgsConstructor
public class RestTemplateConfig {

  private final GenericApplicationContext applicationContext;
  private final AsimioTechProperties asimioTechProperties;

  @PostConstruct
  public void registerRestTemplateBeans() {
    this.asimioTechProperties.getRestTemplates().stream()
      .forEach(restTemplateProps -> this.registerRestTemplateBeans(restTemplateProps));
  }

  private void registerRestTemplateBeans(AsimioTechProperties.RestTemplateProperties restTemplateProps) {
    PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = this.registerPoolingHttpClientConnectionManagerBean(restTemplateProps);
    RequestConfig requestConfig = this.registerRequestConfigBean(restTemplateProps);
    CloseableHttpClient httpClient = this.registerApacheHttpClientBean(poolingHttpClientConnectionManager, requestConfig, restTemplateProps);
    this.registerRestTemplateBean(httpClient, restTemplateProps);
  }

  private PoolingHttpClientConnectionManager registerPoolingHttpClientConnectionManagerBean(AsimioTechProperties.RestTemplateProperties restTemplateProps) {
    PoolingHttpClientConnectionManager result = this.poolingHttpClientConnectionManager(restTemplateProps);
    this.applicationContext.registerBean(
      String.format("poolingHttpClientConnectionManager%s", StringUtils.capitalize(restTemplateProps.getBeanId())),
      PoolingHttpClientConnectionManager.class,
      () -> result
    );
    return result;
  }

  private PoolingHttpClientConnectionManager poolingHttpClientConnectionManager(
    AsimioTechProperties.RestTemplateProperties restTemplateProps) {

    PoolingHttpClientConnectionManager result = new PoolingHttpClientConnectionManager();
    result.setMaxTotal(restTemplateProps.getMaxTotal());
    // Default max per route is used in case it's not set for a specific route
    result.setDefaultMaxPerRoute(restTemplateProps.getDefaultMaxPerRoute());

    // Adding individual route configuration when they exist
    restTemplateProps.getRoutes().stream()
      .forEach(route -> this.addNewRoute(result, route));

    return result;
  }

  private void addNewRoute(PoolingHttpClientConnectionManager connectionManager, RouteProperties route) {
    HttpHost host = new HttpHost(route.getScheme(), route.getHost(), route.getPort());
    // Max per route for a specific host route
    connectionManager.setMaxPerRoute(new HttpRoute(host), route.getMaxPerRoute());
  }

  private RequestConfig registerRequestConfigBean(AsimioTechProperties.RestTemplateProperties restTemplateProps) {
    RequestConfig result = this.requestConfig(restTemplateProps);
    this.applicationContext.registerBean(
      String.format("requestConfig%s", StringUtils.capitalize(restTemplateProps.getBeanId())),
      RequestConfig.class,
      () -> result
    );
    return result;
  }

  private RequestConfig requestConfig(AsimioTechProperties.RestTemplateProperties restTemplateProps) {
    RequestConfig result = RequestConfig.custom()
      .setConnectionRequestTimeout(restTemplateProps.getConnRequestTimeoutMillis(), TimeUnit.MILLISECONDS)
      .setConnectTimeout(restTemplateProps.getConnTimeoutMillis(), TimeUnit.MILLISECONDS)
      .setResponseTimeout(restTemplateProps.getSocketTimeoutMillis(), TimeUnit.MILLISECONDS)
      .build();
    return result;
  }

  private CloseableHttpClient registerApacheHttpClientBean(
    PoolingHttpClientConnectionManager poolingHttpClientConnectionManager,
    RequestConfig requestConfig,
    AsimioTechProperties.RestTemplateProperties restTemplateProps) {

    CloseableHttpClient result = this.apacheHttpClient(poolingHttpClientConnectionManager, requestConfig);
    this.applicationContext.registerBean(
      String.format("closeableHttpClient%s", StringUtils.capitalize(restTemplateProps.getBeanId())),
      CloseableHttpClient.class,
      () -> result
    );
    return result;
  }

  private CloseableHttpClient apacheHttpClient(
    PoolingHttpClientConnectionManager poolingHttpClientConnectionManager,
    RequestConfig requestConfig) {

    CloseableHttpClient result = HttpClientBuilder.create()
      .setConnectionManager(poolingHttpClientConnectionManager)
      .setDefaultRequestConfig(requestConfig)
      .build();
    return result;
  }

  private RestTemplate registerRestTemplateBean(
    HttpClient httpClient,
    AsimioTechProperties.RestTemplateProperties restTemplateProps) {

    RestTemplate result = this.restTemplate(httpClient);
    this.applicationContext.registerBean(restTemplateProps.getBeanId(), RestTemplate.class, () -> result);
    return result;
  }

  private RestTemplate restTemplate(HttpClient httpClient) {
    HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
    requestFactory.setHttpClient(httpClient);
    return new RestTemplate(requestFactory);
  }

}


Usage

ClassThatSendsHttpRequests.java:

@RequiredArgsConstructor
public class ClassThatSendsHttpRequests {

  @Qualifier("restTemplate1")
  private final RestTemplate restTemplate1;

// ...
}

and

AnotherClassThatSendsHttpRequests.java:

@RequiredArgsConstructor
public class AnotherClassThatSendsHttpRequests {

  @Qualifier("restTemplate2")
  private final RestTemplate restTemplate2;

// ...
}

or

SomeClassThatSendsHttpRequests.java:

@RequiredArgsConstructor
public class SomeClassThatSendsHttpRequests {

  @Qualifier("restTemplate1")
  private final RestTemplate restTemplate1;

  @Qualifier("restTemplate2")
  private final RestTemplate restTemplate2;

// ...
}


Source code at: