springboot学习记录之RestTemplate

学习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请求的一种轻量级技术实现

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,724评论 6 342
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,380评论 25 707
  • spring官方文档:http://docs.spring.io/spring/docs/current/spri...
    牛马风情阅读 1,646评论 0 3
  • 妞妞从苏州来看我,一起逛街吃饭。然后在今天早上送她去火车站。并没有预想地那么激动和快乐。 甚至,回来的路上我在想,...
    秋落巴士阅读 424评论 3 1