RestTemplate 是客户端访问 RESTful 服务的核心类。它在概念上类似于 Spring 中的其他模板类,如 JdbcTemplate 和 JmsTemplate 及 其他 Spring 组合项目中的模板类。RestTemplate 的行为是通过提供回调方法及配置 HttpMessageConverter 进行自定义的,HttpMessageConverter 将对象封送到 Http 请求体中,并将任何响应解组成为一个对象。

1. RestTemplate

在 Java 中调用 RESTful 服务通常使用工具类来完成,例如 Apache HttpComponents 中的 HttpClient。对于常见的 REST 操作来说 ,这种方法太低级,如下所示:

String uri = "http://example.com/hotels/1/bookings";

PostMethod post = new PostMethod(uri);
String request = // create booking request content
post.setRequestEntity(new StringRequestEntity(request));

httpClient.executeMethod(post);

if (HttpStatus.SC_CREATED == post.getStatusCode()) {
    Header location = post.getRequestHeader("Location");
    if (location != null) {
        System.out.println("Created new booking at :" + location.getValue());
    }
}

RestTemplate 提供了更高级别的方法,这些方法与六种主要的 Http 方法一一对应,它使得大部分 RESTful 服务的调用在一行内完成,以获得最佳实践。

表格 1.1. RestTemplate 方法概述

HTTP 方法

RestTemplate 方法

DELETE

delete

GET

getForObject getForEntity

HEAD

headForHeaders(String url, String… uriVariables)

OPTIONS

optionsForAllow(String url, String… uriVariables)

POST

postForLocation(String url, Object request, String… uriVariables)

postForObject(String url, Object request, Class responseType, String… uriVariables)

PUT

put(String url, Object request, String…uriVariables)

PATCH and others

exchange execute

RestTemplate 方法的名称遵循命名约定,第一部分指出正在调用什么HTTP方法,第二部分指出返回的内容。例如,getForObject() 方法将执行GET,将HTTP响应转换为指定的对象类型,并返回该对象。如果在处理 Http 请求的过程中出现问题,将抛出一个类型为 RestClientException 的异常;可以通过将一个 ResponseErrorHandler 实现插入到 RestTemplate 来更改此行为。

exchange 和 execute 方法是上面列出的方法的通用版本,可以支持其他组合和方法,如 HTTP PATCH。但是,请注意,底层HTTP库也必须支持所需的组合。JDK HttpURLConnection 不支持 PATCH 方法,但 Apache HttpComponents HttpClient 4.2以上版本支持。还可以使用 ParameterizedTypeReference(一个能够捕获和传递泛型信息的类)来让 RestTemplate 将响应读取为一个泛型类型对象。

这些方法的输入及输出对象与 HTTP 消息之间的相互转换由 HttpMessageConverter 实例来完成。主要的 mime 类型会注册默认的 Converters,你也可以编写自己的 Converters,并通过设置 messageConverters 属性进行注册。注册到模板的默认转换器实例有 ByteArrayHttpMessageConverter,StringHttpMessageConverter,FormHttpMessageConverter 和SourceHttpMessageConverter。如果使用 MarshallingHttpMessageConverter 或 MappingJackson2HttpMessageConverter,则需要设置 messageConverters 属性覆盖这些默认值。

每个方法都有两种形式的 URI 模板参数:与变量个数相同长度的 String 参数或 Map<String, String>。例如:

String result = restTemplate.getForObject(
        "http://example.com/hotels/{hotel}/bookings/{booking}", String.class,"42", "21");

使用变量长度的参数及

Map<String, String> vars = Collections.singletonMap("hotel", "42");
String result = restTemplate.getForObject(
        "http://example.com/hotels/{hotel}/rooms/{hotel}", String.class, vars);

使用一个 Map<String, String>。

你可以调用无参的构造方法来创建一个 RestTemplate 实例。这将使用 java.net 包中的标准Java类作为底层实现来创建HTTP请求。可以通过指定一个 ClientHttpRequestFactory 实现来进行覆盖。Spring 提供了 HttpComponentsClientHttpRequestFactory 实现来使用 Apache HttpComponents HttpClient 创建请求。

前面使用 Apache HttpComponents HttpClient 实现的例子可以直接使用 RestTemplate 重写为:

uri = "http://example.com/hotels/{id}/bookings";
RestTemplate template = new RestTemplate();
Booking booking = // create booking object
URI location = template.postForLocation(uri, booking, "1");

如果要使用 Apache HttpComponents 代替原生的 java.net 的话,请按如下所示构建 RestTemplate:

RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory());

Apache HttpClient 支持 gzip 编码。如果要使用它,按如下所示构建 HttpComponentsClientHttpRequestFactory:

HttpClient httpClient = HttpClientBuilder.create().build();
ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
RestTemplate restTemplate = new RestTemplate(requestFactory);

常见的回调接口为 RequestCallback ,它在 execute 方法执行时被调用。

public <T> T execute(String url, HttpMethod method, RequestCallback requestCallback,
        ResponseExtractor<T> responseExtractor, String... uriVariables)

// also has an overload with uriVariables as a Map<String, String>.

RequestCallback 接口定义如下:

public interface RequestCallback {
 void doWithRequest(ClientHttpRequest request) throws IOException;
}

它可以操作请求头,并写入请求体。当使用 execute 方法时,你不需要再担心资源的管理,模板将始终关闭请求并处理所有错误。

1.1. URI 的使用

对每一个主要的 Http 方法,RestTemplate 提供了 String 类型的 URI 或 java.net.URI 两种类型作为方法的第一个参数。

String 类型的 URI 可以接受的模板参数分为两种,一种是与变量个数相同的 String 参数,另一种是 Map<String, String>。同时,它假定 URL 字符串没有被编码过且需要进行编码。示例如下:

restTemplate.getForObject("http://example.com/hotel list", String.class);

将在 http://example.com/hotel%20list 上执行 GET 请求。这意味着,如果输入的 URL 已经被编码过,那它可能会被再次编码,如:http://example.com/hotel%20list 将会成为 http://example.com/hotel%2520list。如果这个结果不符合预期,请使用 java.net.URI 替换字符串类型的 URI。

UriComponentsBuilder 类可以用来构建和编码URI,包括对 URI 模板的支持。例如:

UriComponents uriComponents = UriComponentsBuilder.fromUriString(
        "http://example.com/hotels/{hotel}/bookings/{booking}").build()
        .expand("42", "21")
        .encode();

URI uri = uriComponents.toUri();

或者分别指定每个 URI 组件:

UriComponents uriComponents = UriComponentsBuilder.newInstance()
        .scheme("http").host("example.com").path("/hotels/{hotel}/bookings/{booking}").build()
        .expand("42", "21")
        .encode();

URI uri = uriComponents.toUri();

1.2. 处理请求及响应头

除了上述方法之外,RestTemplate 还具有 exchange() 方法,可以用于基于 HttpEntity 类的任意 HTTP 方法执行。

最重要的是,exchange() 可以用户添加请求头并读取响应头。例如:

HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("MyRequestHeader", "MyValue");
HttpEntity<?> requestEntity = new HttpEntity(requestHeaders);

HttpEntity<String> response = template.exchange(
        "http://example.com/hotels/{hotel}",
        HttpMethod.GET, requestEntity, String.class, "42");

String responseHeader = response.getHeaders().getFirst("MyResponseHeader");
String body = response.getBody();

在上面的例子中,我们首先准备了一个包含 MyRequestHeader 头的请求实体。接着我们接收到响应,并读取 MyResponseHeader 及响应体。

1.3. Jackson JSON 视图支持

可以指定一个 Jackson JSON View 来序列化对象的部分属性。例如:

MappingJacksonValue value = new MappingJacksonValue(new User("eric", "7!jd#h23"));
value.setSerializationView(User.WithoutPasswordView.class);
HttpEntity<MappingJacksonValue> entity = new HttpEntity<MappingJacksonValue>(value);
String s = template.postForObject("http://example.com/user", entity, String.class);

2. HTTP 消息转换

方法 getForObject(),postForLocation() 及 put() 中的输入输出对象与 HTTP 消息之间的相互转换都需要使用到 HttpMessageConverters。HttpMessageConverter 接口的定义如下,你可以更好地理解其功能:

public interface HttpMessageConverter<T> {

    // Indicate whether the given class and media type can be read by this converter.
    boolean canRead(Class<?> clazz, MediaType mediaType);

    // Indicate whether the given class and media type can be written by this converter.
    boolean canWrite(Class<?> clazz, MediaType mediaType);

    // Return the list of MediaType objects supported by this converter.
    List<MediaType> getSupportedMediaTypes();

    // Read an object of the given type from the given input message, and returns it.
    T read(Class<T> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException;

    // Write an given object to the given output message.
    void write(T t, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException;

}

针对主要媒体(mime)类型的实现在框架中已经提供,默认情况下会注册到客户端的 RestTemplate 及服务器端的 AnnotationMethodHandlerAdapter 中。以下部分将介绍 HttpMessageConverters 的实现。对于所有转换器,默认的媒体类型都可以通过设置 supportedMediaTypes 属性进行覆盖。

2.1. StringHttpMessageConverter

一个可以从 HTTP 请求和响应中读取和写入 Strings 的 HttpMessageConverter 实现。默认情况下,此转换器支持所有文本媒体类型(text/ *),并使用 text/plain 的 Content-Type 进行写入。

2.2. FormHttpMessageConverter

一个可以从 HTTP 请求和响应中读取和写入表单数据的 HttpMessageConverter 实现。默认情况下,此转换器读取和写入媒体类型 application/x-www-form-urlencoded。表单数据会读入及写出到一个 MultiValueMap<String, String>中。

2.3. ByteArrayHttpMessageConverter

一个可以从 HTTP 请求和响应中读取和写入字节数组的 HttpMessageConverter 实现。默认情况下,此转换器支持所有媒体类型(*/*),并以Content-Type 为 application/octet-stream 的形式进行写入。可以通过设置 supportedMediaTypes 属性进行覆盖,同时重写 getContentType(byte[]) 方法。

2.4. MarshallingHttpMessageConverter

一个可以读写 XML 的 HttpMessageConverter 实现。该转换器需要使用 org.springframework.oxm 中的 Marshaller 及 Unmarshaller 接口,这些可以通过构造函数及 bean 属性进行注入。默认情况下,此转换器支持(text/xml)和(application/xml)。

2.5. MappingJackson2HttpMessageConverter

一个可以使用 Jackson 的 ObjectMapper 读取和写入 JSON 的 HttpMessageConverter 实现。可以根据需要使用 Jackson 提供的注释来定制 JSON 映射。需要进一步控制时,可以通过 ObjectMapper 属性注入自定义 ObjectMapper,以便为特定类型提供自定义的 JSON 序列化器/反序列化器。默认情况下,此转换器支持(application/json)。

2.6. MappingJackson2XmlHttpMessageConverter

一个可以使用 Jackson XML 扩展的 XmlMapper 来读写 XML 的 HttpMessageConverter 实现。可以根据需要通过使用 JAXB 或 Jackson 提供的注释来定制 XML 映射。需要进一步控制时,可以通过 ObjectMapper 属性注入自定义的 XmlMapper,以便为特定类型提供自定义的 XML 序列化器/反序列化器。默认情况下,此转换器支持(application/xml)。

2.7. SourceHttpMessageConverter

一个可以从 HTTP 请求和响应读取和写入 javax.xml.transform.Source 的 HttpMessageConverter 实现。只支持DOMSource,SAXSource 和 StreamSource。 默认情况下,此转换器支持(text/xml)和(application/xml)。

2.8. BufferedImageHttpMessageConverter

一个可以从 HTTP 请求和响应中读取和写入 java.awt.image.BufferedImage 的 HttpMessageConverter 实现。该转换器使用 Java/IO API 实现媒体类型的读取和写入。

3. 异步 RestTemplate

Web 应用程序通常需要调用外部的 REST 服务。HTTP 特性和同步调用可能会在为这些需求扩展应用程序时带来挑战:可能会阻塞多个线程,等待远程 HTTP 响应。

AsyncRestTemplate 与 RestTemplate 的 API 非常相似,这些 API 之间的主要区别是 AsyncRestTemplate 返回的是 ListenableFuture 包装器,而不是具体的结果 。

之前的 RestTemplate 示例可以转换为:

// async call
Future<ResponseEntity<String>> futureEntity = template.getForEntity(
    "http://example.com/hotels/{hotel}/bookings/{booking}", String.class, "42", "21");

// get the concrete result - synchronous call
ResponseEntity<String> entity = futureEntity.get();

ListenableFuture 接收完成时的回调:

ListenableFuture<ResponseEntity<String>> futureEntity = template.getForEntity(
    "http://example.com/hotels/{hotel}/bookings/{booking}", String.class, "42", "21");

// register a callback
futureEntity.addCallback(new ListenableFutureCallback<ResponseEntity<String>>() {
    @Override
    public void onSuccess(ResponseEntity<String> entity) {
        //...
    }

    @Override
    public void onFailure(Throwable t) {
        //...
    }
});

默认情况下,AsyncRestTemplate 构造函数会注册一个 SimpleAsyncTaskExecutor 来执行 HTTP 请求。 当处理大量 short-lived 请求时,线程池的 TaskExecutor 实现(如ThreadPoolTaskExecutor)可能是一个不错的选择。

参考链接: http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#rest-client-access