restTemplate使用及源码分析


最近使用restTemplate实现了一个服务对另一个服务中的Rest接口的调用,这里总结下其用法,并分析下源码,话不多说这就开始。

 

一、RestTemplate简单使用

 

1》首先是RestTemplate的配置文件:

package cn.com.ctsi.busiobj.config;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

@Configuration
public class RestTemplateConfig{

    @Bean
    public RestTemplate restTemplate(@Qualifier("simpleClientHttpRequestFactory") ClientHttpRequestFactory factory){
        return new RestTemplate(factory);
    }

    @Bean
    public ClientHttpRequestFactory simpleClientHttpRequestFactory(){
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setReadTimeout(5000);//ms
        factory.setConnectTimeout(15000);//ms
        return factory;
    }
}

1.使用@Configuration表名这是一个配置文件

2.使用RestTemplate的构造方法:

public RestTemplate(ClientHttpRequestFactory requestFactory) {
  this();
  setRequestFactory(requestFactory);
}

ClientHttpRequestFactory对超时时间等属性进行设置。

3.ClientHttpRequestFactory接口有4个实现类,分别是:

  1).AbstractClientHttpRequestFactoryWrapper 用来装配其他request factory的抽象类。
  2).CommonsClientHttpRequestFactory 允许用户配置带有认证和http连接池的httpclient,已废弃,推荐用HttpComponentsClientHttpRequestFactory。
  3).HttpComponentsClientHttpRequestFactory 同2.
  4).SimpleClientHttpRequestFactory 接口的一个简单实现,可配置proxy,connectTimeout,readTimeout等参数。
 
本例中我们使用@Qualifer注解指明使用SimpleClientHttpRequestFactory
 

2》RestTemplate的调用

Service层调用方式如下:

@Service
public class CdmApiUtil {

    @Autowired
    RestTemplate restTemplate;


public CdmRes sendCdmReq(CdmReq cctReq)throws Exception{ try{
            CdmRes res =restTemplate.postForObject(cdmBaseUrl+tableCreateUrl,cctReq,CdmRes.class);
            Assert.notNull(cdmCreateTemplateTableRes,"调用接口,返回为null");
            int code= res.getStatus().getCode();
            Assert.isTrue((code==Status.CODE_SUCCESS),"sendCreateCdmTableReq 创建业务数据表失败,"+ res.getStatus().getDesc());return res;
        }catch (Exception e){
            logger.error("sendCdmReq,"+e.getMessage());
            throw new Exception("调用cdm接口失败,"+e.getMessage());
        }
    }

}

这里我们使用了RestTemplate的postForObject方法发送请求,此方法中的参数:

1)调用rest接口的url路径,

2)拼接的请求实体对象,

3)返回的响应的class实体对象

至此就完成了对远程rest接口的简单调用。

 

二、RestTemplate使用详解

 

转自:https://blog.csdn.net/u012702547/article/details/77917939

GET请求

在RestTemplate中,发送一个GET请求,我们可以通过如下两种方式:

第一种:getForEntity

@Override
    public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables)
            throws RestClientException {

        RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
        ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
        return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
    }

    @Override
    public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables)
            throws RestClientException {

        RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
        ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
        return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
    }

    @Override
    public <T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType) throws RestClientException {
        RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
        ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
        return execute(url, HttpMethod.GET, requestCallback, responseExtractor);
    }

 

 

getForEntity方法的返回值是一个ResponseEntity<T>ResponseEntity<T>是Spring对HTTP请求响应的封装,包括了几个重要的元素,如响应码、contentType、contentLength、响应消息体等。比如下面一个例子:

@RequestMapping("/gethello")
public String getHello() {
  ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://HELLO-SERVICE/hello", String.class);
    String body = responseEntity.getBody();
    HttpStatus statusCode = responseEntity.getStatusCode();
    int statusCodeValue = responseEntity.getStatusCodeValue();
    HttpHeaders headers = responseEntity.getHeaders();
    StringBuffer result = new StringBuffer();
    result.append("responseEntity.getBody():").append(body).append("<hr>")
            .append("responseEntity.getStatusCode():").append(statusCode).append("<hr>")
            .append("responseEntity.getStatusCodeValue():").append(statusCodeValue).append("<hr>")
            .append("responseEntity.getHeaders():").append(headers).append("<hr>");
    return result.toString();
}

关于这段代码:

  • getForEntity的第一个参数为我要调用的服务的地址,这里我调用了服务提供者提供的/hello接口,注意这里是通过服务名调用而不是服务地址,如果写成服务地址就没法实现客户端负载均衡了。
  • getForEntity第二个参数String.class表示我希望返回的body类型是String
  • 拿到返回结果之后,将返回结果遍历打印出来

最终显示结果如下:

这里写图片描述

有时候我在调用服务提供者提供的接口时,可能需要传递参数,有两种不同的方式,如下:

@RequestMapping("/sayhello")
public String sayHello() {
    ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://HELLO-SERVICE/sayhello?name={1}", String.class, "张三");
    return responseEntity.getBody();
}
@RequestMapping("/sayhello2")
public String sayHello2() {
    Map<String, String> map = new HashMap<>();
    map.put("name", "李四");
    ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://HELLO-SERVICE/sayhello?name={name}", String.class, map); return responseEntity.getBody();
}
  • 可以用一个数字做占位符,最后是一个可变长度的参数,来一一替换前面的占位符
  • 也可以前面使用name={name}这种形式,最后一个参数是一个map,map的key即为前边占位符的名字,map的value为参数值

第一个调用地址也可以是一个URI而不是字符串,这个时候我们构建一个URI即可,参数神马的都包含在URI中了,如下:

@RequestMapping("/sayhello3")
public String sayHello3() {
    UriComponents uriComponents = UriComponentsBuilder.fromUriString("http://HELLO-SERVICE/sayhello?name={name}").build().expand("王五").encode();
    URI uri = uriComponents.toUri();
    ResponseEntity<String> responseEntity = restTemplate.getForEntity(uri, String.class);
    return responseEntity.getBody();
}

通过Spring中提供的UriComponents来构建Uri即可。

当然,服务提供者不仅可以返回String,也可以返回一个自定义类型的对象,比如我的服务提供者中有如下方法:

@RequestMapping(value = "/getbook1", method = RequestMethod.GET)
public Book book1() {
    return new Book("三国演义", 90, "罗贯中", "花城出版社");
}

 

对于该方法我可以在服务消费者中通过如下方式来调用:

@RequestMapping("/book1")
public Book book1() {
    ResponseEntity<Book> responseEntity = restTemplate.getForEntity("http://HELLO-SERVICE/getbook1", Book.class);
    return responseEntity.getBody();
}

运行结果如下:

这里写图片描述

第二种:getForObject

getForObject函数实际上是对getForEntity函数的进一步封装,如果你只关注返回的消息体的内容,对其他信息都不关注,此时可以使用getForObject,举一个简单的例子,如下:

@RequestMapping("/book2")
public Book book2() {
    Book book = restTemplate.getForObject("http://HELLO-SERVICE/getbook1", Book.class);
    return book;
}

getForObject也有几个重载方法,如下:

@Override
    public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
        RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
        HttpMessageConverterExtractor<T> responseExtractor =
                new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger);
        return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
    }

    @Override
    public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
        RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
        HttpMessageConverterExtractor<T> responseExtractor =
                new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger);
        return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
    }

    @Override
    public <T> T getForObject(URI url, Class<T> responseType) throws RestClientException {
        RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
        HttpMessageConverterExtractor<T> responseExtractor =
                new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger);
        return execute(url, HttpMethod.GET, requestCallback, responseExtractor);
    }

 

这几个重载方法参数的含义和getForEntity一致,我就不再赘述了。

 

POST请求

在RestTemplate中,POST请求可以通过如下三个方法来发起:

第一种:postForEntity

 

该方法和get请求中的getForEntity方法类似,如下例子:

@RequestMapping("/book3")
public Book book3() {
    Book book = new Book();
    book.setName("红楼梦");
    ResponseEntity<Book> responseEntity = restTemplate.postForEntity("http://HELLO-SERVICE/getbook2", book, Book.class);
    return responseEntity.getBody();
}
  • 方法的第一参数表示要调用的服务的地址
  • 方法的第二个参数表示上传的参数
  • 方法的第三个参数表示返回的消息体的数据类型

我这里创建了一个Book对象,这个Book对象只有name属性有值,将之传递到服务提供者那里去,服务提供者代码如下:

@RequestMapping(value = "/getbook2", method = RequestMethod.POST)
public Book book2(@RequestBody Book book) {
    System.out.println(book.getName());
    book.setPrice(33);
    book.setAuthor("曹雪芹");
    book.setPublisher("人民文学出版社");
    return book;
}

 

服务提供者接收到服务消费者传来的参数book,给其他属性设置上值再返回,调用结果如下:

postForEntity的其他重载方法如下:

@Override
    public <T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables)
            throws RestClientException {

        RequestCallback requestCallback = httpEntityCallback(request, responseType);
        ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
        return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
    }

    @Override
    public <T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)
            throws RestClientException {

        RequestCallback requestCallback = httpEntityCallback(request, responseType);
        ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
        return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
    }

    @Override
    public <T> ResponseEntity<T> postForEntity(URI url, Object request, Class<T> responseType) throws RestClientException {
        RequestCallback requestCallback = httpEntityCallback(request, responseType);
        ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
        return execute(url, HttpMethod.POST, requestCallback, responseExtractor);
    }

 

这些方法的参数含义和getForEntity参数的含义一致,不再赘述。

第二种:postForObject

如果你只关注,返回的消息体,可以直接使用postForObject。用法和getForObject一致。

 

@Override
    public <T> T postForObject(String url, Object request, Class<T> responseType, Object... uriVariables)
            throws RestClientException {

        RequestCallback requestCallback = httpEntityCallback(request, responseType);
        HttpMessageConverterExtractor<T> responseExtractor =
                new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger);
        return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
    }

    @Override
    public <T> T postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)
            throws RestClientException {

        RequestCallback requestCallback = httpEntityCallback(request, responseType);
        HttpMessageConverterExtractor<T> responseExtractor =
                new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger);
        return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
    }

    @Override
    public <T> T postForObject(URI url, Object request, Class<T> responseType) throws RestClientException {
        RequestCallback requestCallback = httpEntityCallback(request, responseType);
        HttpMessageConverterExtractor<T> responseExtractor =
                new HttpMessageConverterExtractor<T>(responseType, getMessageConverters());
        return execute(url, HttpMethod.POST, requestCallback, responseExtractor);
    }

 

第三种:postForLocation

postForLocation也是提交新资源,提交成功之后,返回新资源的URI,postForLocation的参数和前面两种的参数基本一致,只不过该方法的返回值为Uri,这个只需要服务提供者返回一个Uri即可,该Uri表示新资源的位置。

PUT请求

在RestTemplate中,PUT请求可以通过put方法调用,put方法的参数和前面介绍的postForEntity方法的参数基本一致,只是put方法没有返回值而已。举一个简单的例子,如下:

@RequestMapping("/put")
public void put() {
    Book book = new Book();
    book.setName("红楼梦");
    restTemplate.put("http://HELLO-SERVICE/getbook3/{1}", book, 99);
}

book对象是我要提交的参数,最后的99用来替换前面的占位符{1}

DELETE请求

delete请求我们可以通过delete方法调用来实现,如下例子:

@RequestMapping("/delete")
public void delete() {
    restTemplate.delete("http://HELLO-SERVICE/getbook4/{1}", 100);
}

delete方法也有几个重载的方法,不过重载的参数和前面基本一致,不赘述。

 

 

三、源码分析

以postForObject为例进行源码分析:

@Override
    @Nullable
    public <T> T postForObject(URI url, @Nullable Object request, Class<T> responseType)
            throws RestClientException {

        RequestCallback requestCallback = httpEntityCallback(request, responseType);//1.
        HttpMessageConverterExtractor<T> responseExtractor =
                new HttpMessageConverterExtractor<>(responseType, getMessageConverters());//2.
        return execute(url, HttpMethod.POST, requestCallback, responseExtractor);//3.
    }

 

httpEntityCallback(@Nullable Object requestBody, Type responseType)方法:

protected <T> RequestCallback httpEntityCallback(@Nullable Object requestBody, Type responseType) {
        return new HttpEntityRequestCallback(requestBody, responseType);
    }

 

private 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;
            }
        }

1)如果传递进来的requestBody是HttpEntity类型则将其强制转型为HttpEntity<?>类型;

2)否则如果requestBody不为null,则将其转换成HttpEntity格式

3)否则如果requestBody为null,则创建一个没有请求头和请求体的空HttpEntity。

 

HttpEntity实体类如下:

public class HttpEntity<T> {

    /**
     * The empty {@code HttpEntity}, with no body or headers.
     */
    public static final HttpEntity<?> EMPTY = new HttpEntity<>();


    private final HttpHeaders headers;

    @Nullable
    private final T body;

    /**
     * Create a new, empty {@code HttpEntity}.
     */
    protected HttpEntity() {
        this(null, null);
    }

    /**
     * Create a new {@code HttpEntity} with the given body and no headers.
     * @param body the entity body
     */
    public HttpEntity(T body) {
        this(body, null);
    }

    /**
     * Create a new {@code HttpEntity} with the given headers and no body.
     * @param headers the entity headers
     */
    public HttpEntity(MultiValueMap<String, String> headers) {
        this(null, headers);
    }

    /**
     * Create a new {@code HttpEntity} with the given body and headers.
     * @param body the entity body
     * @param headers the entity headers
     */
    public HttpEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers) {
        this.body = body;
        HttpHeaders tempHeaders = new HttpHeaders();
        if (headers != null) {
            tempHeaders.putAll(headers);
        }
        this.headers = HttpHeaders.readOnlyHttpHeaders(tempHeaders);
    }


    /**
     * Returns the headers of this entity.
     */
    public HttpHeaders getHeaders() {
        return this.headers;
    }

    /**
     * Returns the body of this entity.
     */
    @Nullable
    public T getBody() {
        return this.body;
    }

    /**
     * Indicates whether this entity has a body.
     */
    public boolean hasBody() {
        return (this.body != null);
    }

....... }

 即为将requestBody写成HttpEntity的请求头、请求体的形式。

 

下面我们来看第二步:

官方对HttpMessageConverterExtractor的解释是:

Response extractor that uses the given {@linkplain HttpMessageConverter entity converters}
* to convert the response into a type {@code T}

即能够将返回的相应转换为某个特定的类型responseType。

 

第三步:

首先将String类型的URI转换为URI类型,然后execute方法底层调用doExecute方法。

@Override
    public <T> T execute(String url, HttpMethod method, RequestCallback requestCallback,
            ResponseExtractor<T> responseExtractor, Map<String, ?> uriVariables) throws RestClientException {

        URI expanded = getUriTemplateHandler().expand(url, uriVariables);
        return doExecute(expanded, method, requestCallback, responseExtractor);
    }

 

    /**
     * Execute the given method on the provided URI.
     * <p>The {@link ClientHttpRequest} is processed using the {@link RequestCallback};
     * the response with the {@link ResponseExtractor}.
     * @param url the fully-expanded URL to connect to
     * @param method the HTTP method to execute (GET, POST, etc.)
     * @param requestCallback object that prepares the request (can be {@code null})
     * @param responseExtractor object that extracts the return value from the response (can be {@code null})
     * @return an arbitrary object, as returned by the {@link ResponseExtractor}
     */
    protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback,
            ResponseExtractor<T> responseExtractor) throws RestClientException {

        Assert.notNull(url, "'url' must not be null");
        Assert.notNull(method, "'method' must not be null");
        ClientHttpResponse response = null;
        try {
            ClientHttpRequest request = createRequest(url, method);
            if (requestCallback != null) {
                requestCallback.doWithRequest(request);
            }
            response = request.execute();
            handleResponse(url, method, response);
            if (responseExtractor != null) {
                return responseExtractor.extractData(response);
            }
            else {
                return null;
            }
        }
        catch (IOException ex) {
            String resource = url.toString();
            String query = url.getRawQuery();
            resource = (query != null ? resource.substring(0, resource.indexOf(query) - 1) : resource);
            throw new ResourceAccessException("I/O error on " + method.name() +
                    " request for \"" + resource + "\": " + ex.getMessage(), ex);
        }
        finally {
            if (response != null) {
                response.close();
            }
        }
    }

doExecute方法:

1.首先使用请求的url和method(post 或者get)构造出一个ClientHttpRequest。

2.requestCallback.doWithRequest将之前的requestBody requestHeader放入此ClientHttpRequest中;

3.调用request的execute方法获得response,调用handleResponse方法处理response中存在的error;

4.使用ResponseExtractor的extraData方法将返回的response转换为某个特定的类型;

5.最后记得关闭ClientHttpResponse资源,这样就完成了发送请求并获得对应类型的返回值的全部过程。

 

 

四、另外一种方式:使用RestTemplateUtil使用restTemplate

 
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package ctsi.mts.common.mvc.util;

import com.fasterxml.jackson.databind.DeserializationFeature;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.RestTemplate;

public class RestTemplateUtil {
    private static Logger logger = LoggerFactory.getLogger(RestTemplateUtil.class);

    public RestTemplateUtil() {
    }

    private static RestTemplate getRestTemplate() {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        converter.getObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        List<HttpMessageConverter<?>> lstConverters = new ArrayList();
        lstConverters.add(new StringHttpMessageConverter(Charset.forName("utf-8")));
        lstConverters.add(converter);
        RestTemplate rt = new RestTemplate();
        rt.setMessageConverters(lstConverters);
        return rt;
    }

    public static String getForStringResult(String url) {
        return (String)get(url, String.class, (Map)null);
    }

    public static String getForStringResult(String url, Map<String, ?> urlVariables) {
        return (String)get(url, String.class, urlVariables);
    }

    public static <T> T get(String url, Class<T> responseType) {
        return get(url, responseType, (Map)null);
    }

    public static <T> T get(String url, Class<T> responseType, Map<String, ?> urlVariables) {
        logger.info("[rest get] {}", url);
        logger.info("[responseType] {}", responseType);
        Object result;
        if (urlVariables == null) {
            result = getRestTemplate().getForObject(url, responseType, new Object[0]);
        } else {
            Iterator var4 = urlVariables.entrySet().iterator();

            while(var4.hasNext()) {
                Entry entry = (Entry)var4.next();
                logger.info("[params] {} = {}", entry.getKey(), entry.getValue());
            }

            result = getRestTemplate().getForObject(url, responseType, urlVariables);
        }

        logger.info("[result] {}", result);
        return result;
    }

    public static String postForStringResult(String url, Object request) {
        return (String)post(url, request, String.class, (Map)null);
    }

    public static String postForStringResult(String url, Object request, Map<String, ?> urlVariables) {
        return (String)post(url, request, String.class, urlVariables);
    }

    public static <T> T post(String url, Object request, Class<T> responseType) {
        return post(url, request, responseType, (Map)null);
    }

    public static <T> T post(String url, Object request, Class<T> responseType, Map<String, ?> urlVariables) {
        logger.info("[rest post] {}", url);
        logger.info("[request] {}", request);
        logger.info("[responseType] {}", responseType);
        Object result;
        if (urlVariables == null) {
            result = getRestTemplate().postForObject(url, request, responseType, new Object[0]);
        } else {
            Iterator var5 = urlVariables.entrySet().iterator();

            while(var5.hasNext()) {
                Entry entry = (Entry)var5.next();
                logger.info("[params] {} = {}", entry.getKey(), entry.getValue());
            }

            result = getRestTemplate().postForObject(url, request, responseType, urlVariables);
        }

        logger.info("[result] {}", result);
        return result;
    }
}
 

看了下,大致只有getForString  getForObject  postForString postForObject没有forEntity类型,对restTemplate的readTimeOut proxy等属性没有要求时可通过RestTemplateUtil来获取restTemplate对象。








免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM