大家好,我是修真院成都11期学员,今天为大家讲一下restTemplate的介绍和使用。

1、背景介绍

Spring RestTemplate 是 Spring 提供的用于访问 Rest 服务的客户端,RestTemplate 提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率,所以很多客户端比如 Android或者第三方服务商都是使用 RestTemplate 请求 restful 服务。

2、知识剖析

调用 RestTemplate 的默认构造函数,RestTemplate 对象在底层通过使用 java.net 包下的实现创建 HTTP 请求,可以通过使用 ClientHttpRequestFactory 指定不同的HTTP请求方式。默认使用 SimpleClientHttpRequestFactory,是 ClientHttpRequestFactory 实现类。如下流程:

1)使用默认构造方法new一个实例

RestTemplate template = new RestTemplate();

2)RestTemplate 内部通过调用 doExecute 方法,首先就是获取 ClientHttpRequest

3)RestTemplate 实现了抽象类 HttpAccessor ,所以可以调用父类的 createRequest

private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();

public ClientHttpRequestFactory getRequestFactory() {undefined

return this.requestFactory;

}

protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {undefined

ClientHttpRequest request = getRequestFactory().createRequest(url, method);

if (logger.isDebugEnabled()) {undefined

logger.debug("Created " + method.name() + " request for \"" + url + "\"");

}

return request;

}

4)SimpleClientHttpRequestFactory 实现了 ClientHttpRequest,同时实现方法

注意 bufferRequestBody 是可以在 RestTemplate 设置,是标志是否使用缓存流的形式,默认是 true,缺点是当发送大量数据时,比如put/post的保存和修改,那么可能内存消耗严重。所以这时候可以设置 RestTemplate.setBufferRequestBody(false);

即使用 SimpleStreamingClientHttpRequest 来实现。

5)openConnection 没什么文章,而是 prepareConnection 则是大有文章,这里我们分两个版本来说,因为我们一开始使用 4.1.1 的时候不能使用带请求体的delete,可是在 4.3.2 版本则可以使用,所以特别区分了这两个版本的代码,如下:

SimpleClientHttpRequestFactory -- 4.1.1 版本的代码默认

delete connection.setDoOutput = fase

如果设置false,然后后面又去获取输出流时,会发生如下错误 sun 包的 HttpURLConnection

if(!this.doOutput) {undefined

throw new ProtocolException(

"cannot write to a URLConnection if doOutput=false - call setDoOutput(true)"

);

}

SimpleClientHttpRequestFactory -- 4.3.2 版本的代码默认

delete connection.setDoOutput = fase

DoOutput 的属性作用是可以使用 conn.getOutputStream().write() ,这样就能发送请求体了

6)接着执行 requestCallback.doWithRequest(request);

RequestCallback 封装了请求体和请求头对象,也就是说在该对象里面可以拿到我们需要的请求参数,在执行 doWithRequest 时,有一个非常重要的步骤,他和前面Connection发送请求体有着密切关系,我们知道请求头就是 SimpleBufferingClientHttpRequest.addHeaders 方法,那么请求体 bufferedOutput 是如何赋值的呢?就是在 doWithRequest 里面,如下 StringHttpMessageConverter (其他 MessageConvert 也一样,这里也是经常乱码的原因)

其中 s 就是请求体,HttpOutputMessage 对象就是我们准备的 ClientHttpRequest 对象,也就是上面的 SimpleBufferingClientHttpRequest extends AbstractBufferingClientHttpRequest

这样,先调用父类的流方法,把内容写入流中,然后调用父类的 executeInternal方法在调用自身的该方法 executeInternal ,如下一步

7)接着执行 response = request.execute();

然后使用实例 SimpleBufferingClientHttpRequest 封装请求体和请求头

SimpleBufferingClientHttpRequest -- 4.1.1 版本的代码默认

delete 时通过前面设置的 DoOutput 参数和是否可以设置输出流来判断是否需要发送请求体

如果是 delete 请求,那么很明显 DoOutput = false,所以不会有封装请求体的过程,即不执行

FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream());

所以服务端无法获取到请求体,会出现 HttpMessageNotReadableException: Required request body is missing

SimpleBufferingClientHttpRequest -- 4.3.2 版本的代码默认

delete 时通过请求方式和是否有请求体对象来判断是否需要发送请求体

如果是delete请求,首先设置 DoOutput = true,然后根据是否有请求体数据,然后封装请求体

FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream());

8)最后解析response

接着就是 response 的解析了,主要还是 Error 的解析。

handleResponseError(method, url, response);

3、RestTemplate 的配置项

1)setBufferRequestBody 是否是否缓冲流来存储请求体,默认true

2)setProxy 设置代理对象

3)setChunkSize 设置每次传输字节长度,与 setBufferRequestBody(false) 结合使用

4)setConnectTimeout 设置连接超时时间,默认 -1

5)setReadTimeout 设置读取内容超时时间,默认 -1

6)setOutputStreaming 设置Connection是否设置输出流程

7)setTaskExecutor 设置异步回调执行器

4、RestTemplate 设置 RequestFactory

其实任何有连接的地方都会有连接池的概念,比如数据库连接等,这里也不例外,肯定也会有,RestTemplate 默认有两种工厂对象实现方式,都是 ClientHttpRequestFactory 的子类。如下

1)SimpleClientHttpRequestFactory 底层使用 java.net.HttpUrlConnection,可配置证书

2)HttpComponentsClientHttpRequestFactory 底层使用Apache HttpClient访问远程的Http服务,使用HttpClient同样可以配置连接池和证书等信息,而且功能更强大,配置项更多。

5、RequestFactory 的配置方式

1)使用XML配置,就是配置JavaBean

2)使用代码配置,就是初始化这个对象

无论上面那种方式配置,都是配置外壳 RestTemplate,真正发送请求的 request 对象其实都是由工厂管理的,所以我们不关心连接池的管理,只是配置连接池初始化的一些参数而已。

这个可以参考:

Spring提供的用于访问Rest服务的客户端:RestTemplate实践 - WEB服务/RPC/SOA - 软件开发 - 深度开源

6、请求参数的传递

7、关于网上说的无法发送delete请求体

HttpMessageNotReadableException: Required request body is missing

Spring MVC 的 @RequestBody 只支持RestTemplate 的 POST 和 PUT

但是 RestTemplate 的 delete 方法并不支持传入请求体(Request Body)。经测试,通过调用 RestTemplate 类的exchange(String url, HttpMethod method, HttpEntity<?> requestEntity, Class<ResponseResult> responseType, Object... uriVariables)  方法,将 method 指定为 org.springframework.http.HttpMethod.DELETE,并传入 requestEntity(请求体) 对象时,在服务端得到的 Request Body 仍然为 null。可见 RestTemplate 默认并不支持对 DELETE 方法使用请求体。

通过查阅资料发现 RestTemplate 默认是使用 spring 自身的 SimpleClientHttpRequestFactory 创建请求对象和对其进行相关设置(如请求头、请求体等),它只支持 PUT 和 POST 方法带请求体,RestTemplate 的 DELETE 方法不支持传入请求体是因为 JDK 中 HttpURLConnection 对象的 delete 方法不支持传入请求体(如果对 HttpURLConnection 对象的 delete 方法传入请求体,在运行时会抛出 IOException)。

从代码中也看到了 Spring 对 delete 做了判断,如果是 4.1.1 及以前的版本,确实是会出现上面的问题,但是当我使用 4.3.2 之后的版本,发现完全可以发送请求体,这里面的变化就是前者在代码中把请求体过滤掉了,后者把请求体加上了。至于更细的细节,希望有人能够继续深究下去。

3、常见问题

4、解决方案

5、编码实战

配置缓存管理器

@Configuration//相当于beans标签
@EnableCaching//注解驱动的缓存管理器
public class RedisConfiguration extends CachingConfigurerSupport {
/*    @Autowiredprivate RedisConnectionFactory connectionFactory;*//*** @Description: 指定redis主键生成规则:包名+方法名+参数名列表(原有:参数组合)* @return: org.springframework.cache.interceptor.KeyGenerator* @Date: 2018/6/28 17:11*/
/*    @Bean@Overridepublic KeyGenerator keyGenerator() {return new KeyGenerator() {@Overridepublic Object generate(Object o, Method method, Object... objects) {StringBuffer stringBuffer = new StringBuffer();stringBuffer.append(o.getClass().getName());stringBuffer.append("::" + method.getName() + ":");for (Object object : objects)stringBuffer.append(object.toString());return stringBuffer.toString();}};}*//*** @Description: 缓存管理器* @return: org.springframework.cache.CacheManager* @Date: 2018/6/28 17:12*/@Beanpublic CacheManager cacheManager(RedisConnectionFactory connectionFactory) {//通过连接工厂初始化RedisCacheWriterRedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);ClassLoader loader = this.getClass().getClassLoader();ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer =new GenericJackson2JsonRedisSerializer(om);RedisSerializationContext.SerializationPair<Object> rs =RedisSerializationContext.SerializationPair.fromSerializer(genericJackson2JsonRedisSerializer);//设置value序列化方式,如果自带value序列化方式是jdkSerializer,存在redis之中会添加一些东西,人还看不懂//GenericJackson2JsonRedisSerializer序列化方法存储的大小是jdkSerializer的五分之一,并且是人能够读懂的值RedisCacheConfiguration redisCacheConfiguration =RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(rs);//设置默认过期时间redisCacheConfiguration.entryTtl(Duration.ofDays(1));return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);}/*** @Description: 设置RedisTemplate的序列化方式* @return: org.springframework.data.redis.core.RedisTemplate<java.lang.String , java.lang.Object>* @Date: 2018/6/28 17:13*/@Bean(name = "redisTemplate")public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {RedisTemplate<String, Object> template = new RedisTemplate<>();RedisSerializer<String> redisSerializer = new StringRedisSerializer();ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer =new GenericJackson2JsonRedisSerializer(om);template.setConnectionFactory(factory);//key序列化方式template.setKeySerializer(redisSerializer);//value序列化template.setValueSerializer(genericJackson2JsonRedisSerializer);//value hashmap序列化template.setHashValueSerializer(genericJackson2JsonRedisSerializer);return template;}}

6、扩展思考

7、参考文献

https://blog.csdn.net/guokezhongdeyuzhou/article/details/79789629

https://blog.csdn.net/u011851478/article/details/70239722

https://www.cnblogs.com/fashflying/p/6908028.html

8、更多讨论

restTemplate的介绍和使用相关推荐

  1. Spring Cloud之(四)基于RestTemplate的微服务调用

    四.基于RestTemplate的微服务调用 前面我们已经成功的把第一个小案例跑起来了,其中消费者使用了RestTemplate来调用提供者提供的微服务,下面就来详细的说明一下RestTemplate ...

  2. SpringCloud基础

    文章目录 1. 微服务的基础知识 1.1 单体应用架构 1.2 垂直应用架构 1.3 分布式SOA架构 1.4 微服务架构 1.5 SOA与微服务的关系 1.6 分布式核心知识 1.6.1 分布式中的 ...

  3. SpringCloud系列(一)、服务注册中心Eureka基础

    启动Eureka服务注册中心 1.微服务的注册中心 1.1.注册中心的主要作用 1.2.常见的注册中心 1.3.常见注册中心的异同点 2.Eureka概述 2.1.Eureka的基础知识 2.2.Eu ...

  4. 1、SpringCloud入门篇,综合概述

    SpringCloud 微服务概述 什么是微服务 目前的微服务并没有一个统一的标准,一般是以业务来划分 将传统的一站式应用,拆分成一个个的服务,彻底去耦合,一个微服务就是单功能业务,只做一件事. 与微 ...

  5. 1、SpringCloud从入门到放弃:综合

    SpringCloud 微服务概述 什么是微服务 目前的微服务并没有一个统一的标准,一般是以业务来划分 将传统的一站式应用,拆分成一个个的服务,彻底去耦合,一个微服务就是单功能业务,只做一件事. 与微 ...

  6. SpringCloud Hystrix介绍以及基于RestTemplate与Feign的改造

    一.Hystrix介绍 Hystrix 是Netfix的一个开源的延迟和容错库,用于隔离访问远程系统.服务或者第三方库,防止服务级联失败,从而提升系统的可用性与容错性.Hystrix主要通过以下几点实 ...

  7. Spring Cloud 微服务实战系列-Ribbon入门RestTemplate 介绍

    导语   目前在开发中主要的负载均衡方案分为两种:一种是集中式的负载均衡,在生产者和消费者之间通过F5或者是Nginx来进行负载均衡,而另一种则是在客户端自己进行负载均衡,也就是说请求客户端可以根据自 ...

  8. SpringCloud Sentinel 使用restTemplate的两种配置介绍

    @SentinelResource 主要属性介绍 value:自定义的资源名称.不设置默认为当前类全名.方法名. blockHandler:降级的处理方法名,默认在当前类中匹配.如果指定了blockH ...

  9. Spring Boot 中的 RestTemplate不好用?试试 Retrofit !

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者 | 六点半起床 来源 | juejin.im/post/68 ...

最新文章

  1. 从“优化”、“向社会输送人才”到“毕业”,互联网的高情商裁员
  2. linux xampp常见问题
  3. cart2pol函数
  4. WCF与WebService的区别
  5. pixhawk的姿态控制算法解读
  6. 【终极办法】org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘helloC
  7. 解决IE中img.onload失效的方法
  8. 软件工程概论个人作业02(四则运算2)
  9. linux dev alloc name,深入理解Linux网络技术内幕-设备注册和初始化(二)
  10. 2020年二级计算机考试真题,2020年3月计算机等级考试《二级MS Office高级应用》历年真题-试题答案...
  11. Tomcat Caused by java lang OutOfMemoryError PermGen space
  12. 60-300-022-使用-延迟数据-Flink中allowedLateness详细介绍
  13. 统计素数并求和python_C语言实现的统计素数并求和代码分享
  14. 运维派 企业面试题6 防dos攻击
  15. 赛灵思FPGA功耗实测与XPE模拟计算对比分析
  16. 工信部计算机二级证书有什么用,公务员考试,这三个证书用处大,持证年薪10W+...
  17. 【音视频数据数据处理 12】【H.264篇】解析H.264原始码流中的I帧 / P帧 / B帧数据(暂未解决,本文先放着,来日更新)
  18. java程序制作 exe执行文件
  19. 汽车动力学与控制-轮胎侧偏与车辆动力学方程
  20. java数据结构之数组

热门文章

  1. C语言字符串字母移位
  2. C++中什么时候用new[]申请,可以用delete释放
  3. android ndk 编译c++11,Android NDK的C++11标准支持
  4. spool命令、创建一个表,创建并且copy表,查看别的用户下的表,rowid行地址 索引的时候使用,表的增删改查,删除表,oracle的回收站
  5. 1虚拟机下U盘或磁盘挂载,Ubuntu下的网络配置,图形化界面和命令界面之间的切换,软件源配置
  6. Struts2里的Action返回Json数据
  7. 开源的ESB产品列表信息
  8. 【Python-numpy】range()、np.arange()、np.linspace()、np.logspace()的使用和区别,list和array不同
  9. java面试基础_Java面试基础
  10. Android广播的学习和使用