環(huán)境:Springboot2.6.14
如果你的應(yīng)用程序調(diào)用遠(yuǎn)程REST服務(wù),Spring Boot使用RestTemplate或WebClient使之非常的方便。
RestTemplate
如果需要從應(yīng)用程序調(diào)用遠(yuǎn)程REST服務(wù),可以使用Spring框架的RestTemplate類。由于RestTemplate實例通常需要在使用之前進(jìn)行自定義,Spring Boot不提供任何單個自動配置的RestTemplate bean。但是,它可以自動配置一個RestTemplateBuilder,在需要時可以使用它創(chuàng)建RestTemplate實例。自動配置的RestTemplateBuilder確保合理的HttpMessageConverters應(yīng)用于RestTemplate實例。
以下代碼顯示了一個典型示例:?
@Service
public class RemoteService {
  private final RestTemplate restTemplate ;
  public RemoteService(RestTemplateBuilder restTemplateBuilder) {
    this.restTemplate = restTemplateBuilder.build() ;
  }
  public Details storageService(Long storageId, Integer count) {
    return this.restTemplate.getForObject("http://localhost:8080/storage/deduct/{storageId}/{count}", Boolean.class, storageId, count) ;
  }
}
RestTemplateBuilder包括許多可用于快速配置RestTemplate的有用方法。例如,要添加BASIC身份驗證支持,可以使用
builder.basicAuthentication("admin","123").build()
自定義RestTemplate
RestTemplate定制有三種主要方法,這取決于你希望定制應(yīng)用的范圍。
要使任何自定義的范圍盡可能窄,請注入自動配置的RestTemplateBuilder,然后根據(jù)需要調(diào)用其方法。每個方法調(diào)用都返回一個新的RestTemplateBuilder實例,因此自定義僅影響當(dāng)前builder的使用。
要進(jìn)行應(yīng)用程序范圍的附加定制,請使用RestTemplateCustomizer bean。所有這樣的bean都會自動注冊到自動配置的RestTemplateBuilder中,并應(yīng)用于使用它構(gòu)建的任何模板。
自動配置如下:
public class RestTemplateAutoConfiguration {
  @Bean
  @Lazy
  @ConditionalOnMissingBean
  public RestTemplateBuilder restTemplateBuilder(RestTemplateBuilderConfigurer restTemplateBuilderConfigurer) {
    RestTemplateBuilder builder = new RestTemplateBuilder();
    return restTemplateBuilderConfigurer.configure(builder);
  }
}自定義配置
public class CustomRestTemplateCustomizer implements RestTemplateCustomizer {
  @Override
  public void customize(RestTemplate restTemplate) {
    restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient));
  }
}最后,可以定義自己的RestTemplateBuilder bean。這樣做將替換自動配置的RestTemplateBuilder。如果你希望任何RestTemplateCustomizer bean應(yīng)用于你的自定義Builder,就像自動配置所做的那樣,使用RestTemplateBuilderConfigurer對其進(jìn)行配置。下面的例子公開了一個RestTemplateBuilder,它與Spring Boot的自動配置相匹配,只是還指定了自定義連接和讀取超時:?
@Configuration(proxyBeanMethods = false)
public class CustomRestTemplateBuilderConfiguration {
  @Bean
  public RestTemplateBuilder restTemplateBuilder(RestTemplateBuilderConfigurer configurer) {
    return configurer.configure(new RestTemplateBuilder()).setConnectTimeout(Duration.ofSeconds(5))
      .setReadTimeout(Duration.ofSeconds(2));
  }
}
默認(rèn)SpringBoot提供有RestTemplateBuilderConfigurer?
public class RestTemplateAutoConfiguration {
  @Bean
  @Lazy
  @ConditionalOnMissingBean
  public RestTemplateBuilderConfigurer restTemplateBuilderConfigurer(
      ObjectProvider<HttpMessageConverters> messageConverters,
      ObjectProvider<RestTemplateCustomizer> restTemplateCustomizers,
      ObjectProvider<RestTemplateRequestCustomizer<?>> restTemplateRequestCustomizers) {
    RestTemplateBuilderConfigurer configurer = new RestTemplateBuilderConfigurer();
    configurer.setHttpMessageConverters(messageConverters.getIfUnique());
    configurer.setRestTemplateCustomizers(restTemplateCustomizers.orderedStream().collect(Collectors.toList()));
    configurer.setRestTemplateRequestCustomizers(restTemplateRequestCustomizers.orderedStream().collect(Collectors.toList()));
    return configurer;
  }
}使用系統(tǒng)默認(rèn)的RestTemplateBuilderConfigurer是個明智選擇,它合理配置了系統(tǒng)提供的默認(rèn)HttpMessageConverters。
最極端(也很少使用)的選項是在不使用配置程序的情況下創(chuàng)建自己的RestTemplateBuilder bean。除了替換自動配置的構(gòu)建器之外,這還可以防止使用任何RestTemplateCustomizer bean。
WebClient
如果你的類路徑上有Spring WebFlux,你也可以選擇使用WebClient來調(diào)用遠(yuǎn)程REST服務(wù)。與RestTemplate相比,這個客戶端具有更強(qiáng)的功能性,并且是完全響應(yīng)式的。
Spring Boot創(chuàng)建并預(yù)配置WebClient.Builder。強(qiáng)烈建議將它注入到組件中,并使用它創(chuàng)建WebClient實例。Spring Boot正在配置該構(gòu)建器以共享HTTP資源,以與服務(wù)器相同的方式反映編解碼器設(shè)置,等等。
以下代碼顯示了一個典型示例:?
@Service
public class WebClientService {
  private final WebClient webClient;
  public WebClientService(WebClient.Builder webClientBuilder) {
    this.webClient = webClientBuilder.baseUrl("http://localhost:8080").build() ;
  }
  public Mono<Details> storageService(Long storageId, Integer count) {
    return this.webClient.get().uri("/storage/deduct/{storageId}/{count}", storageId, count).retrieve().bodyToMono(Boolean.class) ;
  }
}
WebClient運行時
Spring Boot將自動檢測使用哪個ClientHttpConnector來驅(qū)動WebClient,這取決于應(yīng)用程序類路徑上可用的庫。目前支持Reactor Netty、Jetty RS客戶端和Apache HttpClient。
源碼?
public interface WebClient {
  static WebClient.Builder builder() {
    return new DefaultWebClientBuilder();
  }
}
final class DefaultWebClientBuilder implements WebClient.Builder {
  private static final boolean reactorClientPresent;
  private static final boolean jettyClientPresent;
  private static final boolean httpComponentsClientPresent;
  static {
    ClassLoader loader = DefaultWebClientBuilder.class.getClassLoader();
    reactorClientPresent = ClassUtils.isPresent("reactor.netty.http.client.HttpClient", loader);
    jettyClientPresent = ClassUtils.isPresent("org.eclipse.jetty.client.HttpClient", loader);
    httpComponentsClientPresent = 
      ClassUtils.isPresent("org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient", loader) && 
      ClassUtils.isPresent("org.apache.hc.core5.reactive.ReactiveDataConsumer", loader);
  }
  public WebClient build() {
    ClientHttpConnector connectorToUse = (this.connector != null ? this.connector : initConnector());
    // ...
    return new DefaultWebClient(...) ;
  }
  private ClientHttpConnector initConnector() {
    if (reactorClientPresent) {
      return new ReactorClientHttpConnector();
    } else if (jettyClientPresent) {
      return new JettyClientHttpConnector();
    } else if (httpComponentsClientPresent) {
      return new HttpComponentsClientHttpConnector();
    }
    throw new IllegalStateException("No suitable default ClientHttpConnector found");
  }
}spring-boot-starter-webflux啟動器默認(rèn)依賴于io.projectreactor.netty:reactor-netty,它帶來了服務(wù)器和客戶端實現(xiàn)。如果選擇使用Jetty作為響應(yīng)式服務(wù)器,則應(yīng)該在Jetty響應(yīng)式HTTP客戶端庫上添加一個依賴項。對服務(wù)器和客戶端使用相同的技術(shù)有其優(yōu)點,因為它將自動在客戶端和服務(wù)器之間共享HTTP資源。
開發(fā)人員可以通過提供自定義的ReactorResourceFactory或JettyResourceFactory bean來覆蓋Jetty和Reactor Netty的資源配置——這將應(yīng)用于客戶端和服務(wù)器。
如果你希望覆蓋客戶端的選擇,可以定義自己的ClientHttpConnector bean并完全控制客戶機(jī)配置。
WebClient自定義
WebClient定制主要有三種方法,具體取決于你希望定制應(yīng)用的范圍。
為了使任何定制的范圍盡可能窄,注入自動配置的WebClient.Builder,然后根據(jù)需要調(diào)用它的方法。WebClient.Builder實例是有狀態(tài)的:builder上的任何更改都反映在隨后用它創(chuàng)建的所有client上。如果你想用同一個builder創(chuàng)建多個客戶端,你也可以考慮用WebClient.Builder other = builder.clone();
對所有WebClient進(jìn)行應(yīng)用程序范圍的附加定制WebClient.Builder實例,你可以聲明WebClientCustomizer
最后,你可以使用原始API:WebClient.create()。在這種情況下,不會應(yīng)用自動配置或WebClientCustomizer。