Java CommunitySpring-BootSpring Boot

springboot学习记录之RestTemplate

2017-09-11  本文已影响3189人  _奔波儿灞_

学习springboot ,RestTemplate的使用场景非常非常多,比如springcloud中的服务消费。

我以前还自己去写http请求相关的交互,用的比较多的是apache httpcomponents ,后来在学springboot的过程中发现Spring的RestTemplate提供了一些更高级别的方法来满足我们的功能。后来就把项目中原来的http交互都改成了RestTemplate。

下面来说说我在学习中的一些记录和遇到的问题(还是在使用的角度,具体源码剖析,大家可以自己翻看源码):

1.RestTemplate 的引入:

先来看下RestTemplate 的类路径:

  org.springframework.web.client.RestTemplate 

可以通过上面的路径看出RestTemplate 是web下,项目中只需要加入spring-web的依赖就可以了。我现在使用的spring版本是4.3.9.RELEASE。由于项目是基于springboot 的

Paste_Image.png

在spring-boot-starter-web中已经有了它的依赖。

2.RestTemplate 构造:

RestTemplate有两个构造方法,分别是:

public RestTemplate() {
          /**
               ...初始化过程
          */
}
 
public RestTemplate(ClientHttpRequestFactory requestFactory) {
     this();
     setRequestFactory(requestFactory);
}

其中,第一个进行默认初始化,没法进行更多的限制和后续处理。如:设置超时时间...。第二个构造方法中可以传入ClientHttpRequestFactory参数,ClientHttpRequestFactory接口的实现类中存在timeout属性等等

SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setConnectTimeout(1000);
requestFactory.setReadTimeout(1000);

RestTemplate restTemplate = new RestTemplate(requestFactory);

我们可以在springboot的某个自定义的configure类中的restTemplate 构造方法上添加


@Bean
RestTemplate restTemplate(){
    SimpleClientHttpRequestFactory requestFactory = new          SimpleClientHttpRequestFactory();
    requestFactory.setConnectTimeout(1000);
    requestFactory.setReadTimeout(1000);

    RestTemplate restTemplate = new RestTemplate(requestFactory);
    return restTemplate;
}

将RestTemplate 实例注入spring容器中。
调用时可以通过:

@Autowired
private RestTemplate  restTemplate ;

来使用。

3.RestTemplate 对HTTP Method的支持:

Paste_Image.png

大家可以在图中看到,Spring的RestTemplate提供了对这么多HTTP method的支持。一般来说大家对GET,POST的使用场景比较多,因此下面以这两个为例,简单的说下它的使用。

4.RestTemplate 使用实例(简单):

GET:

 public <T> T getForObject(String url, Class<T> responseType, Object... urlVariables) throws RestClientException 
 public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> urlVariables) throws RestClientException
 public <T> T getForObject(URI url, Class<T> responseType) throws RestClientException

使用方法:

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);

POST:

public <T> T postForObject(String url, Object request, Class<T> responseType, Object... uriVariables)
            throws RestClientException
public <T> T postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)
            throws RestClientException
public <T> T postForObject(URI url, Object request, Class<T> responseType) throws RestClientException

使用方法:

MultiValueMap<String, String> bodyMap = new LinkedMultiValueMap<String, String>();
bodyMap.setAll(urlVariables);
ResponseClass responseClass = restTemplate.postForObject(CAR_CES_URL, bodyMap, ResponseClass.class);

//更完整的:
 HttpHeaders headers = new HttpHeaders();
        headers.add("X-Auth-Token", "e348bc22-5efa-4299-9142-529f07a18ac9");

        MultiValueMap<String, String> postParameters = new LinkedMultiValueMap<String, String>();
        postParameters.add("owner", "11");
        postParameters.add("subdomain", "aoa");
        postParameters.add("comment", "");

        HttpEntity<MultiValueMap<String, String>> requestEntity  = new HttpEntity<MultiValueMap<String, String>>(postParameters, headers);

        ParseResultVo exchange = null;
        try {
            exchange = restTemplate.postForObject("http://demo",  requestEntity, ParseResultVo.class);
            logger.info(exchange.toString());
        } catch (RestClientException e) {
            logger.info("。。。。");
        }

5.其他相关-异步调用(AsyncRestTemplate):

在很多场景中我们需要异步调用,我们使用RestTemplate的兄弟类AsyncRestTemplate。 AsyncRestTemplate是在Spring4.0中对RestTemplate进行扩展产生的新类,其为客户端提供了异步http请求处理的一种机制,通过返回ListenableFuture对象生成回调机制,以达到异步非阻塞发送http请求。

 public String asyncReq(){  
        String url = "http://localhost:8080/jsonAsync";  
        ListenableFuture<ResponseEntity<JSONObject>> future = asyncRestTemplate.getForEntity(url, JSONObject.class);  
        future.addCallback(new SuccessCallback<ResponseEntity<JSONObject>>() {  
            public void onSuccess(ResponseEntity<JSONObject> result) {  
                System.out.println(result.getBody().toJSONString());  
            }  
        }, new FailureCallback() {  
            public void onFailure(Throwable ex) {  
                System.out.println("onFailure:"+ex);  
            }  
        });  
        return "this is async sample";  
}

我这里使用的是futrue,可以带返回参数的。这是java多线程中的一部分内容。如果有时间我会另起一篇简单的说下Java的多线程。

6.请求ssl

参考我的另一篇文章:
RestTemplate设置headers,访问https实现ssl请求

7.遇到的问题:

我在写微信的请求调用的时候出现了问题,微信在文档中说返回的是json数据。但实际返回确是:text/plain。这时直接使用会出现类型转换的错误:

org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class com.solar.app.model.weixin.WxBaseUserInfo] and content type [text/plain]

但是由于默认构造的 MappingJackson2HttpMessageConverter(大家可以翻看源码) 中的 supportedMediaTypes 只支持:application/json 的 MediaType。
为此我们必须添加对它的支持:

public class WxMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
    public WxMappingJackson2HttpMessageConverter(){
        List<MediaType> mediaTypes = new ArrayList<>();
        mediaTypes.add(MediaType.TEXT_PLAIN);
        setSupportedMediaTypes(mediaTypes);
    }
}

我既不推荐把 WxMappingJackson2HttpMessageConverter 实例当作构造 RestTemplate 时的参数来构造 RestTemplate,也不推荐 使用新的 WxMappingJackson2HttpMessageConverter 替换 RestTemplate 默认构造中创建的 MappingJackson2HttpMessageConverter 实例,因为这两种方式都会导致 Content-Type 为 application/json 的 Json 响应没有转换器来反序列化,所以最佳的方式还是“追加”。

@Bean
RestTemplate restTemplate(){
    RestTemplate restTemplate = new RestTemplate();
    restTemplate.getMessageConverters().add(new WxMappingJackson2HttpMessageConverter());
    return restTemplate;
}

最后贴上一个完整实例:


@Configuration
public class RestTemplateConfig {

    @Bean
    @ConditionalOnMissingBean({ RestOperations.class, RestTemplate.class })
    //Spring Boot的自动配置机制依靠@ConditionalOnMissingBean注解判断是否执行初始化代码,
    // 即如果用户已经创建了bean,则相关的初始化代码不再执行。
    public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
        // return new RestTemplate(factory);

        RestTemplate restTemplate = new RestTemplate(factory);

        // 使用 utf-8 编码集的 conver 替换默认的 conver(默认的 string conver 的编码集为"ISO-8859-1")
        List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
        Iterator<HttpMessageConverter<?>> iterator = messageConverters.iterator();
        while (iterator.hasNext()) {
            HttpMessageConverter<?> converter = iterator.next();
            if (converter instanceof StringHttpMessageConverter) {
                iterator.remove();
            }
        }
        messageConverters.add(new StringHttpMessageConverter(Charset.forName("UTF-8")));

        //解决微信返回text/plain的解析
        restTemplate.getMessageConverters().add(new WxMappingJackson2HttpMessageConverter());

        return restTemplate;
    }

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

7.参考资料:

RestTemplate 微信接口 text/plain HttpMessageConverter
基于AsyncRestTemplate异步HTTP请求的一种轻量级技术实现

上一篇下一篇

猜你喜欢

热点阅读