restTemplate的介绍和使用
大家好,我是修真院成都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的介绍和使用相关推荐
- Spring Cloud之(四)基于RestTemplate的微服务调用
四.基于RestTemplate的微服务调用 前面我们已经成功的把第一个小案例跑起来了,其中消费者使用了RestTemplate来调用提供者提供的微服务,下面就来详细的说明一下RestTemplate ...
- SpringCloud基础
文章目录 1. 微服务的基础知识 1.1 单体应用架构 1.2 垂直应用架构 1.3 分布式SOA架构 1.4 微服务架构 1.5 SOA与微服务的关系 1.6 分布式核心知识 1.6.1 分布式中的 ...
- SpringCloud系列(一)、服务注册中心Eureka基础
启动Eureka服务注册中心 1.微服务的注册中心 1.1.注册中心的主要作用 1.2.常见的注册中心 1.3.常见注册中心的异同点 2.Eureka概述 2.1.Eureka的基础知识 2.2.Eu ...
- 1、SpringCloud入门篇,综合概述
SpringCloud 微服务概述 什么是微服务 目前的微服务并没有一个统一的标准,一般是以业务来划分 将传统的一站式应用,拆分成一个个的服务,彻底去耦合,一个微服务就是单功能业务,只做一件事. 与微 ...
- 1、SpringCloud从入门到放弃:综合
SpringCloud 微服务概述 什么是微服务 目前的微服务并没有一个统一的标准,一般是以业务来划分 将传统的一站式应用,拆分成一个个的服务,彻底去耦合,一个微服务就是单功能业务,只做一件事. 与微 ...
- SpringCloud Hystrix介绍以及基于RestTemplate与Feign的改造
一.Hystrix介绍 Hystrix 是Netfix的一个开源的延迟和容错库,用于隔离访问远程系统.服务或者第三方库,防止服务级联失败,从而提升系统的可用性与容错性.Hystrix主要通过以下几点实 ...
- Spring Cloud 微服务实战系列-Ribbon入门RestTemplate 介绍
导语 目前在开发中主要的负载均衡方案分为两种:一种是集中式的负载均衡,在生产者和消费者之间通过F5或者是Nginx来进行负载均衡,而另一种则是在客户端自己进行负载均衡,也就是说请求客户端可以根据自 ...
- SpringCloud Sentinel 使用restTemplate的两种配置介绍
@SentinelResource 主要属性介绍 value:自定义的资源名称.不设置默认为当前类全名.方法名. blockHandler:降级的处理方法名,默认在当前类中匹配.如果指定了blockH ...
- Spring Boot 中的 RestTemplate不好用?试试 Retrofit !
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者 | 六点半起床 来源 | juejin.im/post/68 ...
最新文章
- 从“优化”、“向社会输送人才”到“毕业”,互联网的高情商裁员
- linux xampp常见问题
- cart2pol函数
- WCF与WebService的区别
- pixhawk的姿态控制算法解读
- 【终极办法】org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘helloC
- 解决IE中img.onload失效的方法
- 软件工程概论个人作业02(四则运算2)
- linux dev alloc name,深入理解Linux网络技术内幕-设备注册和初始化(二)
- 2020年二级计算机考试真题,2020年3月计算机等级考试《二级MS Office高级应用》历年真题-试题答案...
- Tomcat Caused by java lang OutOfMemoryError PermGen space
- 60-300-022-使用-延迟数据-Flink中allowedLateness详细介绍
- 统计素数并求和python_C语言实现的统计素数并求和代码分享
- 运维派 企业面试题6 防dos攻击
- 赛灵思FPGA功耗实测与XPE模拟计算对比分析
- 工信部计算机二级证书有什么用,公务员考试,这三个证书用处大,持证年薪10W+...
- 【音视频数据数据处理 12】【H.264篇】解析H.264原始码流中的I帧 / P帧 / B帧数据(暂未解决,本文先放着,来日更新)
- java程序制作 exe执行文件
- 汽车动力学与控制-轮胎侧偏与车辆动力学方程
- java数据结构之数组
热门文章
- C语言字符串字母移位
- C++中什么时候用new[]申请,可以用delete释放
- android ndk 编译c++11,Android NDK的C++11标准支持
- spool命令、创建一个表,创建并且copy表,查看别的用户下的表,rowid行地址 索引的时候使用,表的增删改查,删除表,oracle的回收站
- 1虚拟机下U盘或磁盘挂载,Ubuntu下的网络配置,图形化界面和命令界面之间的切换,软件源配置
- Struts2里的Action返回Json数据
- 开源的ESB产品列表信息
- 【Python-numpy】range()、np.arange()、np.linspace()、np.logspace()的使用和区别,list和array不同
- java面试基础_Java面试基础
- Android广播的学习和使用