简单说就是:简化了发起HTTP请求以及处理响应的过程,并且支持REST。为什么说简化了呢?
来看两种实现方式
(1)使用java.net包下的URLConnection建立连接
String result= "";
BufferedReaderin = null;
try {
String urlNameString= url +"?" + param;
URL realUrl= new URL(urlNameString);
// 打开和URL之间的连接
URLConnectionconnection = realUrl.openConnection();
// 设置通用的请求属性
connection.setRequestProperty("accept","*/*");
connection.setRequestProperty("connection","Keep-Alive");
connection.setRequestProperty("user-agent",
"Mozilla/4.0(compatible; MSIE 6.0; Windows NT 5.1;SV1)");
// 建立实际的连接
connection.connect();
// 获取所有响应头字段
Map> map = connection.getHeaderFields();
// 遍历所有的响应头字段
for(String key : map.keySet()) {
System.out.println(key+ "--->" + map.get(key));
}
// 定义 BufferedReader输入流来读取URL的响应
in =new BufferedReader(newInputStreamReader(
connection.getInputStream()));
String line;
while ((line = in.readLine())!= null) {
result += line;
}
} catch (Exception e) {
…
}
// 使用finally块来关闭输入流
finally{
// 关闭流
}
(2)使用RestTempalte
String类型的URI支持占位符, 那么最终访问的URI为:http://example.com/hotels/42/bookings/21 但是String有一个小缺陷:String形式的URI会被URL编码两次(URL encode请自行百度),这就要求服务器在获取URI中的参数时主动进行一次解码,但如果服务的提供者不这么做呢?
这时就需要使用不会使用任何编码的java.net.URI
User user =restTemplate.postForObject("http://example.com/hotels/{hotel}/bookings/{booking}",User.class,"11","22");
允许调用者指定HTTP请求的方法(GET,POST,PUT等)
可以在请求中增加body以及头信息,其内容通过参数‘HttpEntity>requestEntity’描述
exchange支持‘含参数的类型’(即泛型类)作为返回类型,该特性通过‘ParameterizedTypeReferenceresponseType’描述。比如:
List a = new ArrayList();
System.out.println(a.getClass());
System.out.println(a.getClass().getGenericSuperclass());
ParameterizedTypeReference pt = new ParameterizedTypeReference>() {};
System.out.println(pt.getType());
得到的结果是:
class java.util.ArrayList
java.util.AbstractList
java.util.ArrayList
这是因为ParameterizedTypeReference
所有的get、post、delete、put、options、head、exchange方法最终调用的都是excute方法
Excute方法只是将String格式的URI转成了java.net.URI,之后调用了doExecute方法。整个调用过程
protected T doExecute(URI url, HttpMethod method, RequestCallback requestCallback,ResponseExtractor responseExtractor) throws RestClientException {…}
这里需要了解两个类: RequestCallback &ResponseExtractor
该接口有两个实现类:
AcceptHeaderRequestCallback:只处理请求头,用于getXXX()方法。
HttpEntityRequestCallback:继承于AcceptHeaderRequestCallback可以处理请求头和body,用于putXXX()、postXXX()和exchange()方法。
该接口有三个实现类:
HeadersExtractor 用于提取请求头。
HttpMessageConverterExtractor 用于提取响应body。
ResponseEntityResponseExtractor 使用HttpMessageConverterExtractor提取body(委托模式),然后将body和响应头、状态封装成ResponseEntity对象。
我们知道,调用reseful接口传递的数据内容是json格式的字符串,返回的响应也是json格式的字符串。然而restTemplate.postForObject方法的请求参数RequestBean和返回参数ResponseBean却都是java类。是RestTemplate通过HttpMessageConverter自动帮我们做了转换的操作。
默认情况下RestTemplate自动帮我们注册了一组HttpMessageConverter用来处理一些不同的contentType的请求。
如StringHttpMessageConverter来处理text/plain;MappingJackson2HttpMessageConverter来处理application/json;MappingJackson2XmlHttpMessageConverter来处理application/xml。
你可以在org.springframework.http.converter包下找到所有spring帮我们实现好的转换器。
如果现有的转换器不能满足你的需求,你还可以实现org.springframework.http.converter.HttpMessageConverter接口自己写一个。
选好了HttpMessageConverter后怎么把它注册到我们的RestTemplate中呢。
RestTemplate restTemplate = new RestTemplate();
//获取RestTemplate默认配置好的所有转换器
List> messageConverters = restTemplate.getMessageConverters();
//默认的MappingJackson2HttpMessageConverter在第7个 先把它移除掉
messageConverters.remove(6);
//添加上GSON的转换器
messageConverters.add(6, new GsonHttpMessageConverter());
有时候我们需要对请求做一些通用的拦截设置,这就可以使用拦截器进行处理。拦截器需要我们实现org.springframework.http.client.ClientHttpRequestInterceptor接口自己写。
举个简单的例子,写一个在header中根据请求内容和地址添加令牌的拦截器。
public class TokenInterceptor implements ClientHttpRequestInterceptor
{
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException
{
//请求地址
String checkTokenUrl = request.getURI().getPath();
//token有效时间
int ttTime = (int) (System.currentTimeMillis() / 1000 + 1800);
//请求方法名 POST、GET等
String methodName = request.getMethod().name();
//请求内容
String requestBody = new String(body);
//生成令牌 此处调用一个自己写的方法,有兴趣的朋友可以自行google如何使用ak/sk生成token,此方法跟本教程无关,就不贴出来了
String token = TokenHelper.generateToken(checkTokenUrl, ttTime, methodName, requestBody);
//将令牌放入请求header中
request.getHeaders().add("X-Auth-Token",token);
return execution.execute(request, body);
}
}
创建RestTemplate实例的时候可以这样向其中添加拦截器
RestTemplate restTemplate = new RestTemplate();
//向restTemplate中添加自定义的拦截器
List list = new ArrayList<>();
list.add(new TokenInterceptor());
restTemplate.setInterceptors(list);