Spring Boot 提供了多种方便的方式来调用远程 REST 服务。 如果您正在开发非阻塞响应式应用程序,并且您使用的是 Spring WebFlux,那么您可以使用 . 如果您更喜欢阻止 API,则可以使用 或 .WebClientRestClientRestTemplateSpring中文文档

Web客户端

如果你的类路径上有Spring WebFlux,我们建议你用它来调用远程REST服务。 该接口提供函数式 API,并且是完全响应式的。 您可以在 Spring Framework 文档的专用部分中了解有关 的更多信息。WebClientWebClientWebClientSpring中文文档

如果您不是在编写响应式 Spring WebFlux 应用程序,则可以使用 RestClient 而不是 . 这提供了一个类似的功能 API,但正在阻塞而不是被动。WebClient

Spring Boot 为您创建并预配置原型 Bean。 强烈建议将其注入您的组件中并使用它来创建实例。 Spring Boot 正在配置该构建器以共享 HTTP 资源,并以与服务器相同的方式反映编解码器设置(请参阅 WebFlux HTTP 编解码器自动配置)等。WebClient.BuilderWebClientSpring中文文档

下面的代码显示了一个典型示例:Spring中文文档

import reactor.core.publisher.Mono;

import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;

@Service
public class MyService {

	private final WebClient webClient;

	public MyService(WebClient.Builder webClientBuilder) {
		this.webClient = webClientBuilder.baseUrl("https://example.org").build();
	}

	public Mono<Details> someRestCall(String name) {
		return this.webClient.get().uri("/{name}/details", name).retrieve().bodyToMono(Details.class);
	}

}
import org.springframework.stereotype.Service
import org.springframework.web.reactive.function.client.WebClient
import reactor.core.publisher.Mono

@Service
class MyService(webClientBuilder: WebClient.Builder) {

	private val webClient: WebClient

	init {
		webClient = webClientBuilder.baseUrl("https://example.org").build()
	}

	fun someRestCall(name: String?): Mono<Details> {
		return webClient.get().uri("/{name}/details", name)
				.retrieve().bodyToMono(Details::class.java)
	}

}

WebClient 运行时

Spring Boot 将根据应用程序类路径上可用的库自动检测要使用哪个库来驱动。 按优先顺序,支持以下客户端:ClientHttpConnectorWebClientSpring中文文档

  1. Reactor Netty(内蒂反应堆)Spring中文文档

  2. Jetty RS 客户端Spring中文文档

  3. Apache HttpClientSpring中文文档

  4. JDK HttpClientSpring中文文档

如果类路径上有多个客户端可用,则将使用首选客户端。Spring中文文档

默认情况下,启动器依赖于,它同时带来了服务器和客户端实现。 如果选择使用 Jetty 作为响应式服务器,则应添加对 Jetty 响应式 HTTP 客户端库 的依赖项。 对服务器和客户端使用相同的技术有其优点,因为它会在客户端和服务器之间自动共享 HTTP 资源。spring-boot-starter-webfluxio.projectreactor.netty:reactor-nettyorg.eclipse.jetty:jetty-reactive-httpclientSpring中文文档

开发人员可以通过提供自定义或 bean 来覆盖 Jetty 和 Reactor Netty 的资源配置 - 这将应用于客户端和服务器。ReactorResourceFactoryJettyResourceFactorySpring中文文档

如果您希望覆盖客户机的该选择,您可以定义自己的 Bean 并完全控制客户机配置。ClientHttpConnectorSpring中文文档

WebClient 自定义

自定义有三种主要方法,具体取决于您希望自定义项应用的范围。WebClientSpring中文文档

若要使任何自定义项的范围尽可能窄,请注入自动配置,然后根据需要调用其方法。 实例是有状态的:构建器上的任何更改都会反映在随后使用它创建的所有客户端中。 如果要使用同一构建器创建多个客户端,还可以考虑使用 克隆构建器。WebClient.BuilderWebClient.BuilderWebClient.Builder other = builder.clone();Spring中文文档

要对所有实例进行应用程序范围的附加定制,您可以在注入点声明 bean 并在本地更改。WebClient.BuilderWebClientCustomizerWebClient.BuilderSpring中文文档

最后,您可以回退到原始 API 并使用 . 在这种情况下,不会应用自动配置或。WebClient.create()WebClientCustomizerSpring中文文档

WebClient SSL 支持

如果您需要自定义 SSL 配置,则可以注入可与构建器方法一起使用的实例。ClientHttpConnectorWebClientWebClientSslapplySpring中文文档

该接口提供对您在 或 文件中定义的任何 SSL 捆绑包的访问。WebClientSslapplication.propertiesapplication.yamlSpring中文文档

下面的代码显示了一个典型示例:Spring中文文档

import reactor.core.publisher.Mono;

import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientSsl;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;

@Service
public class MyService {

	private final WebClient webClient;

	public MyService(WebClient.Builder webClientBuilder, WebClientSsl ssl) {
		this.webClient = webClientBuilder.baseUrl("https://example.org").apply(ssl.fromBundle("mybundle")).build();
	}

	public Mono<Details> someRestCall(String name) {
		return this.webClient.get().uri("/{name}/details", name).retrieve().bodyToMono(Details.class);
	}

}
import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientSsl
import org.springframework.stereotype.Service
import org.springframework.web.reactive.function.client.WebClient
import reactor.core.publisher.Mono

@Service
class MyService(webClientBuilder: WebClient.Builder, ssl: WebClientSsl) {

	private val webClient: WebClient

	init {
		webClient = webClientBuilder.baseUrl("https://example.org")
				.apply(ssl.fromBundle("mybundle")).build()
	}

	fun someRestCall(name: String?): Mono<Details> {
		return webClient.get().uri("/{name}/details", name)
				.retrieve().bodyToMono(Details::class.java)
	}

}
如果您不是在编写响应式 Spring WebFlux 应用程序,则可以使用 RestClient 而不是 . 这提供了一个类似的功能 API,但正在阻塞而不是被动。WebClient

RestClient

如果您没有在应用程序中使用 Spring WebFlux 或 Project Reactor,我们建议您使用来调用远程 REST 服务。RestClientSpring中文文档

该接口提供了一个功能样式块 API。RestClientSpring中文文档

Spring Boot 为您创建并预配置原型 Bean。 强烈建议将其注入您的组件中并使用它来创建实例。 Spring Boot 正在使用适当的 .RestClient.BuilderRestClientHttpMessageConvertersClientHttpRequestFactorySpring中文文档

下面的代码显示了一个典型示例:Spring中文文档

import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;

@Service
public class MyService {

	private final RestClient restClient;

	public MyService(RestClient.Builder restClientBuilder) {
		this.restClient = restClientBuilder.baseUrl("https://example.org").build();
	}

	public Details someRestCall(String name) {
		return this.restClient.get().uri("/{name}/details", name).retrieve().body(Details.class);
	}

}
import org.springframework.boot.docs.io.restclient.restclient.ssl.Details
import org.springframework.stereotype.Service
import org.springframework.web.client.RestClient

@Service
class MyService(restClientBuilder: RestClient.Builder) {

	private val restClient: RestClient

	init {
		restClient = restClientBuilder.baseUrl("https://example.org").build()
	}

	fun someRestCall(name: String?): Details {
		return restClient.get().uri("/{name}/details", name)
				.retrieve().body(Details::class.java)!!
	}

}

RestClient 定制

自定义有三种主要方法,具体取决于您希望自定义项应用的范围。RestClientSpring中文文档

若要使任何自定义项的范围尽可能窄,请注入自动配置,然后根据需要调用其方法。 实例是有状态的:构建器上的任何更改都会反映在随后使用它创建的所有客户端中。 如果要使用同一构建器创建多个客户端,还可以考虑使用 克隆构建器。RestClient.BuilderRestClient.BuilderRestClient.Builder other = builder.clone();Spring中文文档

要对所有实例进行应用程序范围的附加定制,您可以在注入点声明 bean 并在本地更改。RestClient.BuilderRestClientCustomizerRestClient.BuilderSpring中文文档

最后,您可以回退到原始 API 并使用 . 在这种情况下,不会应用自动配置或。RestClient.create()RestClientCustomizerSpring中文文档

RestClient SSL 支持

如果您需要自定义 SSL 配置,则可以注入可与构建器方法一起使用的实例。ClientHttpRequestFactoryRestClientRestClientSslapplySpring中文文档

该接口提供对您在 或 文件中定义的任何 SSL 捆绑包的访问。RestClientSslapplication.propertiesapplication.yamlSpring中文文档

下面的代码显示了一个典型示例:Spring中文文档

import org.springframework.boot.autoconfigure.web.client.RestClientSsl;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;

@Service
public class MyService {

	private final RestClient restClient;

	public MyService(RestClient.Builder restClientBuilder, RestClientSsl ssl) {
		this.restClient = restClientBuilder.baseUrl("https://example.org").apply(ssl.fromBundle("mybundle")).build();
	}

	public Details someRestCall(String name) {
		return this.restClient.get().uri("/{name}/details", name).retrieve().body(Details.class);
	}

}
import org.springframework.boot.autoconfigure.web.client.RestClientSsl
import org.springframework.boot.docs.io.restclient.restclient.ssl.settings.Details
import org.springframework.stereotype.Service
import org.springframework.web.client.RestClient

@Service
class MyService(restClientBuilder: RestClient.Builder, ssl: RestClientSsl) {

	private val restClient: RestClient

	init {
		restClient = restClientBuilder.baseUrl("https://example.org")
				.apply(ssl.fromBundle("mybundle")).build()
	}

	fun someRestCall(name: String?): Details {
		return restClient.get().uri("/{name}/details", name)
				.retrieve().body(Details::class.java)!!
	}

}

如果除了 SSL 捆绑包之外还需要应用其他自定义项,则可以将该类与以下内容一起使用:ClientHttpRequestFactorySettingsClientHttpRequestFactoriesSpring中文文档

import java.time.Duration;

import org.springframework.boot.ssl.SslBundles;
import org.springframework.boot.web.client.ClientHttpRequestFactories;
import org.springframework.boot.web.client.ClientHttpRequestFactorySettings;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;

@Service
public class MyService {

	private final RestClient restClient;

	public MyService(RestClient.Builder restClientBuilder, SslBundles sslBundles) {
		ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.DEFAULTS
			.withReadTimeout(Duration.ofMinutes(2))
			.withSslBundle(sslBundles.getBundle("mybundle"));
		ClientHttpRequestFactory requestFactory = ClientHttpRequestFactories.get(settings);
		this.restClient = restClientBuilder.baseUrl("https://example.org").requestFactory(requestFactory).build();
	}

	public Details someRestCall(String name) {
		return this.restClient.get().uri("/{name}/details", name).retrieve().body(Details.class);
	}

}
import org.springframework.boot.ssl.SslBundles
import org.springframework.boot.web.client.ClientHttpRequestFactories
import org.springframework.boot.web.client.ClientHttpRequestFactorySettings
import org.springframework.stereotype.Service
import org.springframework.web.client.RestClient
import java.time.Duration

@Service
class MyService(restClientBuilder: RestClient.Builder, sslBundles: SslBundles) {

	private val restClient: RestClient

	init {
		val settings = ClientHttpRequestFactorySettings.DEFAULTS
				.withReadTimeout(Duration.ofMinutes(2))
				.withSslBundle(sslBundles.getBundle("mybundle"))
		val requestFactory = ClientHttpRequestFactories.get(settings)
		restClient = restClientBuilder
				.baseUrl("https://example.org")
				.requestFactory(requestFactory).build()
	}

	fun someRestCall(name: String?): Details {
		return restClient.get().uri("/{name}/details", name).retrieve().body(Details::class.java)!!
	}

}

RestTemplate

Spring Framework 的 RestTemplate 类早于许多应用程序用于调用远程 REST 服务的经典方式。 当你有不想迁移到的现有代码时,或者因为你已经熟悉 API,你可以选择使用。RestClientRestTemplateRestClientRestTemplateSpring中文文档

由于实例在使用前通常需要自定义,因此 Spring Boot 不提供任何单个自动配置的 Bean。 但是,它确实会自动配置 ,该配置可用于在需要时创建实例。 自动配置可确保将合理和适当的应用于实例。RestTemplateRestTemplateRestTemplateBuilderRestTemplateRestTemplateBuilderHttpMessageConvertersClientHttpRequestFactoryRestTemplateSpring中文文档

下面的代码显示了一个典型示例:Spring中文文档

import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class MyService {

	private final RestTemplate restTemplate;

	public MyService(RestTemplateBuilder restTemplateBuilder) {
		this.restTemplate = restTemplateBuilder.build();
	}

	public Details someRestCall(String name) {
		return this.restTemplate.getForObject("/{name}/details", Details.class, name);
	}

}
import org.springframework.boot.web.client.RestTemplateBuilder
import org.springframework.stereotype.Service
import org.springframework.web.client.RestTemplate

@Service
class MyService(restTemplateBuilder: RestTemplateBuilder) {

	private val restTemplate: RestTemplate

	init {
		restTemplate = restTemplateBuilder.build()
	}

	fun someRestCall(name: String): Details {
		return restTemplate.getForObject("/{name}/details", Details::class.java, name)!!
	}

}

RestTemplateBuilder包括许多可用于快速配置 . 例如,要添加 BASIC 身份验证支持,可以使用 .RestTemplatebuilder.basicAuthentication("user", "password").build()Spring中文文档

RestTemplate 自定义

自定义有三种主要方法,具体取决于您希望自定义项应用的范围。RestTemplateSpring中文文档

若要使任何自定义项的范围尽可能窄,请注入自动配置,然后根据需要调用其方法。 每个方法调用都会返回一个新实例,因此自定义项仅影响生成器的此使用。RestTemplateBuilderRestTemplateBuilderSpring中文文档

要进行应用程序范围的附加定制,请使用 bean。 所有此类 Bean 都会自动注册到自动配置的 bean,并应用于使用它构建的任何模板。RestTemplateCustomizerRestTemplateBuilderSpring中文文档

以下示例显示了一个定制器,该定制器为除以下主机之外的所有主机配置代理的使用:192.168.0.5Spring中文文档

import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
import org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner;
import org.apache.hc.client5.http.routing.HttpRoutePlanner;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.protocol.HttpContext;

import org.springframework.boot.web.client.RestTemplateCustomizer;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

public class MyRestTemplateCustomizer implements RestTemplateCustomizer {

	@Override
	public void customize(RestTemplate restTemplate) {
		HttpRoutePlanner routePlanner = new CustomRoutePlanner(new HttpHost("proxy.example.com"));
		HttpClient httpClient = HttpClientBuilder.create().setRoutePlanner(routePlanner).build();
		restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient));
	}

	static class CustomRoutePlanner extends DefaultProxyRoutePlanner {

		CustomRoutePlanner(HttpHost proxy) {
			super(proxy);
		}

		@Override
		protected HttpHost determineProxy(HttpHost target, HttpContext context) throws HttpException {
			if (target.getHostName().equals("192.168.0.5")) {
				return null;
			}
			return super.determineProxy(target, context);
		}

	}

}
import org.apache.hc.client5.http.classic.HttpClient
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder
import org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner
import org.apache.hc.client5.http.routing.HttpRoutePlanner
import org.apache.hc.core5.http.HttpException
import org.apache.hc.core5.http.HttpHost
import org.apache.hc.core5.http.protocol.HttpContext
import org.springframework.boot.web.client.RestTemplateCustomizer
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory
import org.springframework.web.client.RestTemplate

class MyRestTemplateCustomizer : RestTemplateCustomizer {

	override fun customize(restTemplate: RestTemplate) {
		val routePlanner: HttpRoutePlanner = CustomRoutePlanner(HttpHost("proxy.example.com"))
		val httpClient: HttpClient = HttpClientBuilder.create().setRoutePlanner(routePlanner).build()
		restTemplate.requestFactory = HttpComponentsClientHttpRequestFactory(httpClient)
	}

	internal class CustomRoutePlanner(proxy: HttpHost?) : DefaultProxyRoutePlanner(proxy) {

		@Throws(HttpException::class)
		public override fun determineProxy(target: HttpHost, context: HttpContext): HttpHost? {
			if (target.hostName == "192.168.0.5") {
				return null
			}
			return  super.determineProxy(target, context)
		}

	}

}

最后,您可以定义自己的 Bean。 这样做将替换自动配置的构建器。 如果您希望将任何 Bean 应用于定制构建器,就像自动配置一样,请使用 . 以下示例公开了一个与 Spring Boot 的自动配置相匹配的操作,只是还指定了自定义连接和读取超时:RestTemplateBuilderRestTemplateCustomizerRestTemplateBuilderConfigurerRestTemplateBuilderSpring中文文档

import java.time.Duration;

import org.springframework.boot.autoconfigure.web.client.RestTemplateBuilderConfigurer;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyRestTemplateBuilderConfiguration {

	@Bean
	public RestTemplateBuilder restTemplateBuilder(RestTemplateBuilderConfigurer configurer) {
		return configurer.configure(new RestTemplateBuilder())
			.setConnectTimeout(Duration.ofSeconds(5))
			.setReadTimeout(Duration.ofSeconds(2));
	}

}
import org.springframework.boot.autoconfigure.web.client.RestTemplateBuilderConfigurer
import org.springframework.boot.web.client.RestTemplateBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import java.time.Duration

@Configuration(proxyBeanMethods = false)
class MyRestTemplateBuilderConfiguration {

	@Bean
	fun restTemplateBuilder(configurer: RestTemplateBuilderConfigurer): RestTemplateBuilder {
		return configurer.configure(RestTemplateBuilder()).setConnectTimeout(Duration.ofSeconds(5))
			.setReadTimeout(Duration.ofSeconds(2))
	}

}

最极端(也很少使用)的选项是在不使用配置程序的情况下创建自己的 bean。 除了替换自动配置的构建器之外,这还可以防止使用任何 Bean。RestTemplateBuilderRestTemplateCustomizerSpring中文文档

RestTemplate SSL 支持

如果需要自定义 SSL 配置,可以将 SSL 捆绑包应用于 ,如本例所示:RestTemplateRestTemplateBuilderSpring中文文档

import org.springframework.boot.docs.io.restclient.resttemplate.Details;
import org.springframework.boot.ssl.SslBundles;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class MyService {

	private final RestTemplate restTemplate;

	public MyService(RestTemplateBuilder restTemplateBuilder, SslBundles sslBundles) {
		this.restTemplate = restTemplateBuilder.setSslBundle(sslBundles.getBundle("mybundle")).build();
	}

	public Details someRestCall(String name) {
		return this.restTemplate.getForObject("/{name}/details", Details.class, name);
	}

}
import org.springframework.boot.docs.io.restclient.resttemplate.Details
import org.springframework.boot.ssl.SslBundles
import org.springframework.boot.web.client.RestTemplateBuilder
import org.springframework.stereotype.Service
import org.springframework.web.client.RestTemplate

@Service
class MyService(restTemplateBuilder: RestTemplateBuilder, sslBundles: SslBundles) {

    private val restTemplate: RestTemplate

    init {
        restTemplate = restTemplateBuilder.setSslBundle(sslBundles.getBundle("mybundle")).build()
    }

    fun someRestCall(name: String): Details {
        return restTemplate.getForObject("/{name}/details", Details::class.java, name)!!
    }

}

RestClient 和 RestTemplate 的 HTTP 客户端检测

Spring Boot 将自动检测要与哪个 HTTP 客户端一起使用,并取决于应用程序类路径上可用的库。 按优先顺序,支持以下客户端:RestClientRestTemplateSpring中文文档

  1. Apache HttpClientSpring中文文档

  2. 码头 HttpClientSpring中文文档

  3. OkHttp(已弃用)Spring中文文档

  4. 简单的 JDK 客户端 (HttpURLConnection)Spring中文文档

如果类路径上有多个客户端可用,则将使用首选客户端。Spring中文文档