restTemplate

1. 基本介绍

RestTemplate 是 Spring 提供的,用于访问Rest服务的同步客户端,提供了一些简单的模板方法API;底层支持多种Http客户端类库,因为RestTemplate只是对其他的HTTP客户端的封装,其本身并没有实现HTTP相关的基础功能,底层实现可以按需配置;常用的有:

  • SimpleClientHttpRequestFactory,默认配置,对应的JDK自带的HttpURLConnection,不支持Http协议的Patch方法,也无法访问Https请求;
  • HttpComponentsClientHttpRequestFactory,对应的是Apache的HttpComponents(注:Apache的HttpClient是前身,后边改名为Components);
  • OkHttp3ClientHttpRequestFactory,对应的是OkHttp。

如果向查看所有的http客户端类库,可以找下ClientHttpRequestFactory接口的实现类:

RestTemplate、Apache的HttpClient、OkHttp比较:

  • RestTemplate 提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率;
  • HttpClient代码复杂,还得操心资源回收等。代码很复杂,冗余代码多,不建议直接使用;
  • okhttp是一个高效且开源的第三方HTTP客户端类库,常用于android中请求网络,是安卓端最火热的轻量级框架;允许所有同一个主机地址的请求共享同一个socket连接,连接池减少请求延时;透明的GZIP压缩减少响应数据的大小,缓存响应内容,避免一些完全重复的请求。

2. 常用方法分析及举例

这一块主要讲一些常用的方法及参数、对于一些重载的方法,其实原理都差不多。

2.1. get请求

除了getForEntity和getForObject外,使用exchange()也可以,前两个是基于它实现的,此处不做介绍

参数包括请求url、响应类型的class、请求参数

  • url:String字符串或者URI对象;常用字符串
  • 响应对象的class实例
    • getForEntity() ==> 响应为ResponseEntity,其中包括请求的响应码和HttpHeaders
    • getForObject() ==> 响应为传入的class对象,只包括响应内容
  • 请求参数:替换url中的占位符,可以使用可变长的Object,也可以使用Map;如果没有,可以不填 其中object是按照占位符的顺序匹配的,map是根据key匹配,如果匹配不上,就报错
// 不带参数的
String url = "localhost:8001/test/method";
Object object = restTemplate.getForObject(url, Object.class); // 带参数的,使用@PathVariable接收
String url = "localhost:8001/test/method/{param1}";
Object object = restTemplate.getForObject(url, Object.class, "param");
// 带参数的,使用@Requestparam接收
String url = "localhost:8001/test/method?param={dd}";
ResponseEntity<Object> result = restTemplate.getForObject(url, Object.class, "param");

2.2. post请求

参数和get请求的相比,就多了第二个参数(Object request),如果使用最后一个参数传参时,和get请求类似,request设置为null就可以,如果使用第二个参数传参时,就需要考虑request的类型,request参数类型必须是实体对象、MultiValueMap、HttpEntity对象的的一种,其他不可以!!!

  • 实体对象传参时,被请求的接口方法上必须使用@RequestBody,接收参数为实体或者Map;
  • HttpEntity传参时,取决于HttpEntity对象的第一个参数,可以为任何类型,包括HashMap;
  • MultiValueMap传参时,接收参数使用注解@RequestBody时,使用一个String类型、名称随意,使用@RequestParam时,使用对应个数的String类型字符串,名称必须和map中的key相同;推荐使用@RequestParam
// 使用MultiValueMap传参
String url = "localhost:8001/test/method";
MultiValueMap<String, String> map= new LinkedMultiValueMap<>(); // 不能使用HashMap,源码中会讲!
map.add("param1", "string1");
map.add("param2", "string2");
ResponseEntity<String> response = restTemplate.postForEntity(url, map , String.class );
// 使用HttpEntity传参
String url = "localhost:8001/test/method";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.add(HttpHeaders.CONTENT_ENCODING, StandardCharsets.UTF_8.toString());
headers.add(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
String string = "param";
HttpEntity<String> entity = new HttpEntity<String>(string,headers);
String result = restTemplate.postForObject(url, entity, String.class);

3. springboot中使用restTemplate步骤

  1. 导入jar包
<!-- springboot web依赖  -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
  1. 编写配置

    使用默认的SimpleClientHttpRequestFactory,也就是java JDK自带的HttpURLConnection;
@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);
factory.setConnectTimeout(5000);
return factory;
}
}
  1. Service层调用
@Service
public class TestService implements ITestService {
@Autowired
RestTemplate restTemplate; @Override
public ResultVo test() throws Exception {
try {
String url = "http://localhhost:9001/test/demo1";
ResponseEntity<Object> objectResponseEntity = restTemplate.postForEntity(url, null, Object.class);
if( 200 == objectResponseEntity.getStatusCodeValue()){
return ResultVo.success();
}
return ResultVo.error();
} catch (Exception e) {
throw new Exception("调用接口失败," + e.getMessage());
}
}
}

4. 源码分析(postForEntity为例)

思考重点:

1. 第二个参数为什么不能直接使用HashMap,而只能使用MultiValueMap?

2. 接收参数时,怎么合理的使用@RequestBody和@RequestParam?

3. restTemplate底层默认使用的是SimpleClientHttpRequestFactory,为什么不支持调用Https接口?

  1. 依次进入方法:postForEntity() -> httpEntityCallback -> HttpEntityRequestCallback


  1. requestBody参数,会判断类型是否是HttpEntity,如果不是,则创建一个HttpEntity类将 requestBody 参数传入,然后查看HttpEntity构造器,具体做了什么?

  1. 可以看到,三个构造方法,上边两个调用的是最下边一个;

    第一个传入的是泛型,也就是传入的Object对象

    第二个传入的是MultiValueMap,这个值是存放Headers的

    所有只需要关注这个泛型,在哪块使用的

  1. 回到postForEntity()方法中,找到调用请求的方法execute,点进去发现是调用方法doExecute(...);

  1. 在doExecute()中

    • 首先使用请求的url和method(post 或者get)构造出一个ClientHttpRequest
    • requestCallback.doWithRequest将之前的requestBody、requestHeader放入此ClientHttpRequest中;
    • 调用request的execute方法获得response,调用handleResponse方法处理response中存在的error
    • 使用ResponseExtractor的extraData方法将返回的response转换为某个特定的类型;
    • 最后关闭ClientHttpResponse资源,这样就完成了发送请求并获得对应类型的返回值的全部过程。

  1. 进入方法getRequestFactory() -> getRequestFactory()可以发现,通过this.requestFactory初始化了SimpleClientHttpRequestFactory();通过方法createRequest(url, method) -> openConnection()发现创建了HttpURLConnection连接,因此默认使用的restTemplate是无法访问Https接口的

  1. 进入方法doWithRequest(request)可以发现,程序会执行第一个else中的逻辑,根据传入的参数,判断requestBodyClass、requestBodyType和MediaType;

    • 如果第二个参数为HashMap或者MultiValueMap时,MediaType为null;
    • 如果是HttpEntity时,requestBodyClass为对应参数的类型,MediaType为封装HttpHeaders中的ContentType值

接下来会遍历所有的HttpMessageConverter,这些对象在RestTemplate的构造函数中被初始化

  1. 在遍历过程中判断是否可以写入,如果能写入则执行写入操作并返回;判断MessageConvertor是否为GenericHttpMessageConverter的子类,是因为写入的方式不同;在这些MessageConvertor中只有GsonHttpMessageConverter是GenericHttpMessageConverter的子类,且排在最后;因此,遍历过程中会先判断前六个convertor,能写入则执行写入,最后才是GsonHttpMessageConvertor。分析所有的HTTPMessageConvertor,可以发现

    • MultiValueMap子类的数据会被AllEncompassingFormHttpMessageConverter处理,将MediaType置为application/x-www-form-urlencoded、将request中的key value通过&=拼接并写入到body中,接收时,可以为@RequestParam、也可以为@RequestBody Http协议中,如果不指定Content-Type,则默认传递的参数就是application/x-www-form-urlencoded类型

    • HashMap类型的数据会被GsonHTTPMessageConvertor处理,将MediaType置为application/json;charset=UTF-8、将request转成json并写入到body中,因此,第二个参数设置为HashMap时,无法设置ContentType值,所有第二个参数无法使用HashMap!但是可以使用HttpEntity对象,将HashMap存放在HttpEntity对象里边,接收参数时,使用@RequestBody




5. restTemplate访问Https接口

restTemplate底层默认使用的是SimpleClientHttpRequestFactory,是基于HttpURLConnection,是不支持调用Https接口的,可以修改为HttpComponentsClientHttpRequestFactory

public class RestTemplateConfig {
@Bean
public RestTemplate getRestTemplate() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
return true;
}
}).build(); SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext,
new String[]{"TLSv1"},
null,
NoopHostnameVerifier.INSTANCE); CloseableHttpClient httpClient = HttpClients.custom()
.setSSLSocketFactory(csf)
.build(); HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(); requestFactory.setHttpClient(httpClient);
return new RestTemplate(requestFactory);
}
}

以前工作中经常使用,但是时不时的就会出现问题,每次都是盲目的百度解决,趁着周末空余时间,详细的学习一下下,如果有不对的地方或者疑问,请大家指正,一起讨论。

RestTemplate-postForObject详解、调用Https接口、源码解析,读懂这一篇文章就够了的更多相关文章

  1. spring事务详解(三)源码详解

    系列目录 spring事务详解(一)初探事务 spring事务详解(二)简单样例 spring事务详解(三)源码详解 spring事务详解(四)测试验证 spring事务详解(五)总结提高 一.引子 ...

  2. Hadoop详解(03)-Hadoop编译源码-了解

    Hadoop详解(03)-Hadoop编译源码-了解 准备工作 CentOS联网 配置CentOS能连接外网.Linux虚拟机ping www.baidu.com 是畅通的 jar包准备(hadoop ...

  3. Sevlet规范:HttpServlet类 和 HttpServletRequest接口 源码解析

    Sevlet规范:HttpServlet类 和 HttpServletRequest接口 源码解析 每博一文案 命运总是不如人愿,但往往是在无数的痛苦总,在重重的矛盾和艰辛中,才是人成熟起来. 你,为 ...

  4. AlexNet 网络详解及Tensorflow实现源码

    版权声明:本文为博主原创文章,未经博主允许不得转载. 1. 图片数据处理 2. 卷积神经网络 2.1. 卷积层 2.2. 池化层 2.3. 全链层 3. AlexNet 4. 用Tensorflow搭 ...

  5. (二十三)原型模式详解(clone方法源码的简单剖析)

    作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 原型模式算是JAVA中最简单 ...

  6. 设计模式之 原型模式详解(clone方法源码的简单剖析)

    作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 原型模式算是JAVA中最简单 ...

  7. 二十三:原型模式详解(clone复制方法源码)

    定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.                 定义比较简单,总结一下是通过实例指定种类,通过拷贝创建对象. 在JAVA语言中使用原型模式是非常 ...

  8. 广告行业中那些趣事系列8:详解BERT中分类器源码

    最新最全的文章请关注我的微信公众号:数据拾光者. 摘要:BERT是近几年NLP领域中具有里程碑意义的存在.因为效果好和应用范围广所以被广泛应用于科学研究和工程项目中.广告系列中前几篇文章有从理论的方面 ...

  9. ThreadLocal类详解:原理、源码、用法

    以下是本文目录: 1.从数据库连接探究 ThreadLocal 2.剖析 ThreadLocal 源码 3. ThreadLocal 应用场景 4. 通过面试题理解 ThreadLocal 1.从数据 ...

  10. 干货:Java多线程详解(内附源码)

      线程是程序执行的最小单元,多线程是指程序同一时间可以有多个执行单元运行(这个与你的CPU核心有关). 在java中开启一个新线程非常简单,创建一个Thread对象,然后调用它的start方法,一个 ...

随机推荐

  1. 动作捕捉系统验证OPT追踪井下无人机的性能

    井下无人机长时间在恶劣环境下执行勘测.救援任务,通讯系统可能会陷入两难的境地--传输高精度坐标伴随着大量耗能.为解决这项难题,中国矿业大学计算机科学和技术学院陈朋朋教授团队提出了一种基于超宽带(UWB ...

  2. Spring Cloud整体架构解析

    Spring Cloud整体架构 Spring Cloud的中文名我们就暂且称呼它为"春云"吧,听上去是多么朴实无华的名字,不过呢一般名字起的低调的都是厉害角色,我们就看看Spri ...

  3. 【白话科普】聊聊 DNS 的那些小知识

    你是否在上网时,遇到过这样的情况:QQ 能正常发送消息,但是网页却打不开,查看网络连接又正常显示.面对这种情况很多小伙伴都感到有些无措.那究竟要怎么处理,这究竟是怎么回事呢? 上网查询得知,一般情况下 ...

  4. hdu 5234

    题意:求在不超过k的情况下,最多可以得到多少价值. 三维dp,结合01背包,第三维就是用来保存在不同的背包容量下能得到的最大价值,也就是第三维有很多状态. #include<iostream&g ...

  5. 基于python+django的酒店预定网站-酒店管理系统

    该系统是基于python+django开发的酒店预定管理系统.适用场景:大学生.课程作业.毕业设计.学习过程中,如遇问题可在github给作者留言. 演示地址 前台地址: http://hotel.g ...

  6. 国庆学go,完成了博客基本功能,迫不及待的发布上线了

    大家好,我是沙漠尽头的狼. 国庆7天,利用带娃之余的空闲时间学习了go,并做了一个不是很完善的博客前台网站. 网站发布地址:https://go.dotnet9.com 源码 边做边上传Github, ...

  7. 青少年CTF训练平台 — CRYPTO通关WP

    A2-Crypto Caesar vxshyk{g9g9g099-hg33-4f60-90gk-ikff1f36953j} 凯撒解码 qsnctf{b9b9b099-cb33-4a60-90bf-df ...

  8. [转帖]Oracle中为什么需要NCHAR

    https://zhuanlan.zhihu.com/p/668402759 Oracle中已经有了char.varchar2等字符类型,为什么又弄出一个nchar.nvarchar2? Oracle ...

  9. KVM命令行Clone虚拟机的快速处理

    KVM命令行Clone虚拟机的快速处理 背景 鲲鹏+银河麒麟的测试环境 想着可以使用 KVM的方式创建虚拟机 virt-manager 有个clone虚拟机的提示, 但是发现没有创建新存储卷下面的磁盘 ...

  10. [转帖]TiDB 环境与系统配置检查

    https://docs-archive.pingcap.com/zh/tidb/v6.0/check-before-deployment 本文介绍部署 TiDB 前的环境检查操作,以下各项操作按优先 ...