目录
前言
Spring Cloud系列: 点击查看Spring Cloud系列文章
RestTemplate
RestTemplate是Spring提供的用于访问Rest服务的客户端,RestTemplate提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率。
访问restful服务,传统的还有Apache的HttpClient。不过相比之下,RestTemplate使用起来更加方便简洁。
RestTemplate常用方法
RestTemplate对不同的HTTP方法,有不同的请求方法,如下图所示
注:exchange和excute可以通用上述HTTP方法,即GET方法可以使用,POST方法也可以使用等
RestTemplate方法的名称遵循命名约定,第一部分指示正在调用的HTTP方法,第二部分指示返回的内容。例如,该方法getForObject()将执行GET,将HTTP响应转换为您选择的对象类型,然后返回该对象。该方法postForLocation() 将执行POST,将给定的对象转换为HTTP请求,然后返回响应HTTP Location标头,可以在其中找到新创建的对象。
ForEntity方法的返回值是一个ResponseEntity,ResponseEntity是Spring对HTTP请求响应的封装,包括了几个重要的元素,如响应码、响应头、contentType、contentLength、响应消息体等。
ForObject方法实际上是对ForEntity函数的进一步封装,ForObject方法会将响应体转换成你想要的对象类型,如果你只关注返回的消息体的内容,对其他信息都不关注,此时可以使用ForObject。
如下:
//第一个参数是url,第二个是请求参数,第三个是返回结果转换的类型,第四个是uri参数(即uri上的参数)
public <T> T postForObject(String url, @Nullable Object request, Class<T> responseType,
Object... uriVariables) throws RestClientException
例:
restTemplate.postForObject(
"http://example.com/hotels/{hotel}/bookings/{booking}", String.class,"42", "21");
HttpMessageConverter
RestTemplate默认使用HttpMessageConverter实例将HTTP消息转换成POJO或者从POJO转换成HTTP消息。默认情况下会注册主mime类型的转换器,但是您也可以编写自己的转换器并通过messageConverters()属性进行注入 。在ForObject方法的responseType 参数,它让你传入一个响应体的对象类型,底层就是用HttpMessageConverter将其做映射。
自定义HttpMessageConverter,实现AbstractHttpMessageConverter接口即可
如下
public class StringToJsonObjectMessageConverter extends AbstractHttpMessageConverter<JSONObject> {
/**
* HttpMessageConvert是否处理接受的这个类
*/
@Override
protected boolean supports(Class<?> clazz) {
return true;
}
/**
*对数据进行处理并返回
*/
@Override
protected JSONObject readInternal(Class<? extends JSONObject> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
return null;
}
/**
*处理如何输出数据到response
*/
@Override
protected void writeInternal(JSONObject jsonObject, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
}
}
2、在restTemplate的MessageConverters属性set进自定义的转换器
@Bean
public RestTemplate restTemplate() {
HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory();
RestTemplate restTemplate = new RestTemplate(httpRequestFactory);
// 设置自定义转换器,第一个参数为转换器的顺序,越小越大,最小为0。add和set方法都可以
restTemplate.getMessageConverters().set(1,new StringToJsonObjectMessageConverter());
return restTemplate;
}
RestTemplate的一些内置转换器
StringHttpMessageConverter
StringHttpMessageConverter可以从HTTP请求和响应中读取和写入字符串的实现。默认情况下,此转换器支持所有文字媒体类型( text/*),写入类型为Content-Type的text/plain。
FormHttpMessageConverter
FormHttpMessageConverter可以从HTTP请求和响应中读取和写入表单数据的实现。默认情况下,此转换器读取和写入媒体类型 application/x-www-form-urlencoded。表单数据可从中读取或写入 MultiValueMap<String, String>。
ByteArrayHttpMessageConverter
ByteArrayHttpMessageConverter可以从HTTP请求和响应读取和写入字节数组的实现。默认情况下,该转换器支持所有介质类型(/),写入类型为Content-Type的application/octet-stream。可以通过设置supportedMediaTypes属性和overriding来覆盖它getContentType(byte[])。
创建RestTemplate
创建RestTemplate很简单,只需要new RestTemplate(),如果使用Spring架构,只需要将创建的RestTemplate实例通过XML或注解的方式注册到Spring容器中即可
如:
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
RestTemplate 有三种构造函数,一个无参,两个有参,如下
public RestTemplate() {}
public RestTemplate(ClientHttpRequestFactory requestFactory) {}
public RestTemplate(List<HttpMessageConverter<?>> messageConverters) {}
创建多个RestTemplate实例
有时候,我们需要两个或者多个不同配置的RestTemplate,那我们可以在配置文件注册多个RestTemplate 的bean
@Configuration
public class MyConfiguration {
@LoadBalanced
@Bean
RestTemplate loadBalanced() {
return new RestTemplate();
}
@Primary
@Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
}
在注入的时候,使用 @Qualifier(“名称”)进行注入,否则会报错
如:
@Autowired
@Qualifier("loadBalanced")
private RestTemplate loadBalanced;
RestTemplate 的负载均衡
RestTemplate有客户端负载均衡功能,那怎么开启 呢?很简单,只需要在RestTemplate的bean上添加@LoadBalanced注解即可
如:
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
注:RestTemplate 是通过拦截器改变请求的URI的方式来指定服务器的,请求的url需要通过服务名调用而不是服务地址,如果写成服务地址就没法实现客户端负载均衡了(在有多个服务提供实例,才有必要用负载均衡)
例:
restTemplate.getForEntity("http://HELLO-SERVICE/hello", String.class);
设置超时时间
设置超时时间,可以直接使用Spring的底层基于HttpClient的HttpComponentsClientHttpRequestFactory,此处设置的是ClientHttpRequestFactory级别的全局超时时间
@Bean
public RestTemplate restTemplate() {
HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory();
//从连接池获取连接的超时时间
httpRequestFactory.setConnectionRequestTimeout(30 * 1000);
//客户端和服务器建立连接的超时时间
httpRequestFactory.setConnectTimeout(30 * 3000);
//读取数据的超时时间
httpRequestFactory.setReadTimeout(30 * 3000);
RestTemplate restTemplate = new RestTemplate(httpRequestFactory);
return restTemplate;
}
创建URI对象
在RestTemplate调用HTTTP服务时,URL可以是String类型 或 URI类型,下面我们就来看一下URI对象如何创建
URI对象的无参构造方法是私有的,不能直接调用。需要使用它的有参构造方法
public URI(String str) throws URISyntaxException {
}
public URI(String scheme, String userInfo, String host, int port, String path, String query, String fragment) throws URISyntaxException {
}
除了使用构造方法,我们还可以通过UriComponentsBuilder构造器来生成URI对象
例:
UriComponents uriComponents = UriComponentsBuilder.fromUriString(
"http://example.com/hotels/{hotel}/bookings/{booking}").build()
.expand("42", "21")//传递uri参数,无参不需要
.encode();
URI uri = uriComponents.toUri();
RestTemplate API使用
getForEntity():返回ResponseEntity对象
/**
* 参数url: String类型 或 URI类型的请求地址
* 参数responseType: 指定返回的实体类型,class对象
* 参数uriVariables: uri参数,可以是变长数组或map
* 返回值:responseType指定的Object类型
*/
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables){}
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables){}
public <T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType){}
例:
ResponseEntity<Book> responseEntity = restTemplate.getForEntity("http://127.0.0.1:8080/getbook?bookname={1}", Book.class, "java");
Book book = responseEntity.getBody(); //响应体转换为Book类型
int statusCodeValue = responseEntity.getStatusCodeValue(); //响应状态码
HttpHeaders headers = responseEntity.getHeaders(); //响应头信息
getForObject():返回指定的Object类型
public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables){}
public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables)
public <T> T getForObject(URI url, Class<T> responseType)
例:
Book book = restTemplate.getForObject("http://127.0.0.1:8080/getbook", Book.class);
POST方法
postForEntity方法
/**
* 参数url: String类型 或 URI类型的请求地址
* 参数request: 请求body,可以是HttpEntity类型(可设置request header),或其它Object类型
* 参数responseType: 指定返回的实体类型,class对象
* 参数uriVariables: uri参数,可以是变长数组或map
* 返回值:ResponseEntity<T>是Spring对HTTP响应的封装,包括了几个重要的元素,如响应码、contentType、contentLength、response header信息,response body信息等
*/
@Override
public <T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables) throws RestClientException {
}
@Override
public <T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
}
@Override
public <T> ResponseEntity<T> postForEntity(URI url, Object request, Class<T> responseType) throws RestClientException {
}
例:以下是使用HttpEntity类型请求参数的例子
//使用HttpEntity 类型的请求参数发送请求,HttpEntity 比一般的Object参数好的地方是,我们可以在HttpEntity 设置一些请求头信息
String url="http://xxx";
//请求头
HttpHeaders httpHeaders=new HttpHeaders();
//设置Content-Type(用于指示资源的MIME类型 media type)在请求中 (如POST 或 PUT),客户端告诉服务器实际发送的数据类型。在响应中,Content-Type标头告诉客户端实际返回的内容的内容类型。
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
//自定义参数
httpHeaders.add("myKey","myValue");
MultiValueMap<String,String>multiValueMap=new LinkedMultiValueMap<>();
multiValueMap.add("name","m");
HttpEntity httpEntity=new HttpEntity(multiValueMap,httpHeaders);
ResponseEntity<String> stringResponseEntity = restTemplate.postForEntity(url, httpEntity, String.class);
//获取请求头
HttpHeaders headers = stringResponseEntity.getHeaders();
//取出请求头的参数,getFirst为获取第一个,get为获取所有
String myKey = headers.getFirst("myKey");
HttpStatus statusCode = stringResponseEntity.getStatusCode();
//响应体自动转换成response类型
String body = stringResponseEntity.getBody();
注意,构造HttpEntity 的body使用MultiValueMap,HttpEntity接受的request类型就是它。
MultiValueMap是Map的一个子类,它的一个key可以存储多个value,因为它的value是一个list集合
public interface MultiValueMap<K, V> extends Map<K, List<V>> {}
restTemplate.postForEntity方法虽然表面上接收的request是@Nullable Object request类型,但是你追踪下去会发现,这个request是用HttpEntity来解析。代码如下
public HttpEntityRequestCallback(@Nullable Object requestBody, @Nullable Type responseType) {
super(responseType);
if (requestBody instanceof HttpEntity) {
this.requestEntity = (HttpEntity<?>) requestBody;
}
else if (requestBody != null) {
this.requestEntity = new HttpEntity<>(requestBody);
}
else {
this.requestEntity = HttpEntity.EMPTY;
}
}
以下为使用对象做请求参数的例子
//直接使用对象作为request参数
restTemplate.postForEntity(url,new RequestPojo(),String.class);
postForObject()
参数一样,只是返回的类型不一样,postForObject直接返回响应体,并转换成responseType
@Override
public <T> T postForObject(String url, Object request, Class<T> responseType, Object... uriVariables)
throws RestClientException {
}
@Override
public <T> T postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)
throws RestClientException {
}
@Override
public <T> T postForObject(URI url, Object request, Class<T> responseType) throws RestClientException {
}
例:
//直接返回了responseType类型的结果,没有请求头等信息
String result = restTemplate.postForObject(url, new RequestPojo(), String.class);
exchange方法
exchange()方法跟上面的getForObject()、getForEntity()、postForObject()、postForEntity()等方法不同之处在于它可以指定请求的HTTP类型。
@Override
public <T> ResponseEntity<T> exchange(String url, HttpMethod method,
@Nullable HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables)
throws RestClientException {
}
@Override
public <T> ResponseEntity<T> exchange(String url, HttpMethod method,
@Nullable HttpEntity<?> requestEntity, Class<T> responseType, Map<String, ?> uriVariables)
throws RestClientException {
}
@Override
public <T> ResponseEntity<T> exchange(URI url, HttpMethod method, @Nullable HttpEntity<?> requestEntity,
Class<T> responseType) throws RestClientException {
}
参数说明:url为请求地址,可以是字符串,也可以是URI对象。method为请求的方法类型,如:HttpMethod.GET表示GET方法,requestEntity表示一个HttpEntity类型的请求对象,responseType为返回的响应类型,uriVariables为URI参数
例:
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();