Spring RestTemplate

简单说就是:简化了发起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");

  • Exchange 方法
      与其它接口的不同:

允许调用者指定HTTP请求的方法(GET,POST,PUT等)

可以在请求中增加body以及头信息,其内容通过参数‘HttpEntityrequestEntity’描述

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并不根据实参而是使用getGenericSuperclass()方法获取其父类的类型(注意这里的new有花括号,是ParameterizedTypeReference的子类),父类的类型通过java.lang.reflect.Type描述,然后通过Type的getActualTypeArguments()获得了父类的实参类型,注意得到的Type类,并不是class类。

  • excute 方法

所有的get、post、delete、put、options、head、exchange方法最终调用的都是excute方法

Excute方法只是将String格式的URI转成了java.net.URI,之后调用了doExecute方法。整个调用过程

  • doExcute方法
protected  T doExecute(URI url, HttpMethod method, RequestCallback requestCallback,ResponseExtractor responseExtractor) throws RestClientException {…}

这里需要了解两个类: RequestCallback &ResponseExtractor

  1. RequestCallback : 用于操作请求头和body,在请求发出前执行。

该接口有两个实现类:

AcceptHeaderRequestCallback:只处理请求头,用于getXXX()方法。
HttpEntityRequestCallback:继承于AcceptHeaderRequestCallback可以处理请求头和body,用于putXXX()、postXXX()和exchange()方法。
  1. ResponseExtractor : 解析HTTP响应的数据,而且不需要担心异常和资源的关闭。

该接口有三个实现类:

HeadersExtractor	用于提取请求头。
HttpMessageConverterExtractor	用于提取响应body。
ResponseEntityResponseExtractor	使用HttpMessageConverterExtractor提取body(委托模式),然后将body和响应头、状态封装成ResponseEntity对象。

手动指定转换器(HttpMessageConverter)

我们知道,调用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());

设置拦截器(ClientHttpRequestInterceptor)

有时候我们需要对请求做一些通用的拦截设置,这就可以使用拦截器进行处理。拦截器需要我们实现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);

你可能感兴趣的:(SpringCloud)