美文网首页Java CommunitySpring-BootSpring Boot
springboot学习记录之RestTemplate

springboot学习记录之RestTemplate

作者: _奔波儿灞_ | 来源:发表于2017-09-11 10:41 被阅读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请求的一种轻量级技术实现

    相关文章

      网友评论

        本文标题:springboot学习记录之RestTemplate

        本文链接:https://www.haomeiwen.com/subject/nspxsxtx.html