转载 关于restTemplate 内部实现
2024-10-12 10:36:02
2016-12-28 by 安静的下雪天 http://www.cnblogs.com/quiet-snowy-day/p/6228198.html
本篇概要
- RestTemplate 类图
- postForEntity 方法处理过程
- requestCallback.doWithRequest 方法处理过程
- responseExtractor.extractData 方法处理过程
- 关于GenericHttpMessageConverter
- 关于RestTemplate 中的转换器列表
RestTemplate类图
RestTemplate 类中省略了静态成员变量、变量的set/get方法以及实现的接口方法,
RestOperations 接口中省略了参数类型及重载的方法。这样既可以保证各个要素尽量在一幅图中展现,又不会影响理解。
postForEntity方法中,创建了两个内部类对象requestCallback和responseExtractor并传递给execute方法,分别用于请求和响应的关键处理。
总结了一下,不管是请求还是响应,这里的关键处理就是明确资源的媒体类型(也就是要明确请求端和响应端交换的信息的格式),
根据媒体类型选择适合的解析器,将消息写入输出流或者从输入流读入。
requestCallback.doWithRequest 处理过程
——内部类AcceptHeaderRequestCallback.doWithRequest的处理。
发送请求时,Http头部需要设置Accept字段,该字段表明了发送请求的这方接受的媒体类型(消息格式),也是响应端要返回的信息的媒体类型(消息格式)。
根据postForEntity方法的第三个参数responseType,程序将选择适合的解析器XXXConverter,并依据该解析器找出所有支持的媒体类型。
——内部类HttpEntityRequestCallback.doWithRequest的处理。
如果是POST请求并且消息体存在时,除了设置Accept字段,还可能需要设置Content-Type字段,该字段表明了所发送请求的媒体类型(消息格式),也是响应端接受的媒体类型(消息格式)。
根据postForEntity方法的第二个参数request,程序将选择适合的解析器XXXConverter,将请求消息写入输出流。
返回顶部
responseExtractor.extractData 处理过程
与请求消息体的处理过程相似。
虽然,postForEntity方法中responseExtractor对象的类型为ResponseEntityResponseExtractor,但是实际执行处理过程是HttpMessageConverterExtractor的对象实例。
在postForObject方法中,则是直接使用了HttpMessageConverterExtractor创建对象。
下图画出的也是HttpMessageConverterExtractor类中的extractData方法的处理过程。
关于GenericHttpMessageConverter
在以上几个方法的梳理过程中,我注意到每次消息解析转换都要作GenericHttpMessageConverter分支判断,为什么呢?
GenericHttpMessageConverter接口继承自HttpMessageConverter接口,二者都是在org.springframework.http.converter路径下。
此包中还有其他几种Converter实现类,看名字就可以猜到主要功能。唯独GenericHttpMessageConverter没猜出来。
于是,我在eclipse中使用Ctrl+Shift+G快捷键搜索了一下它的实现类AbstractGenericHttpMessageConverter。
看到AbstractJackson2HttpMessageConverter类的时候,我好像明白了。
GenericHttpMessageConverter是其他转换器派生类的接口,用于解析特殊格式的资源,比如json,xml等。
返回顶部
关于RestTemplate 中的转换器列表
转换器列表messageConverters是final类型的,由RestTemplate的构造函数赋值。一旦创建了RestTemplate对象,该对象也就同时拥有了一个当前系统支持的转换器列表。
那么,对于需要引用jar包的转换器,RestTemplate是怎么添加转换器实例的呢?
在声明messageConverters列表之前,定义了几个布尔型静态常量,该常量是对某一个特殊类是否可以被加载的判断结果。
在RestTemplate的构造函数中,根据该常量值来判断是否将某个转换器的实例加入到列表中。
由此可知,RestTemplate的初始化顺序:
创建(new)一个RestTemplate实例时,首先装载RestTemplate类,然后按照出现的顺序转载静态变量或代码。
装载完成之后,进行实例化。首先实例化成员变量,然后执行构造函数。
那么,外部引用的类是否可以被加载具体是怎么判断的?
通过ClassUtils.isPresent(String className, ClassLoader classLoader)方法。——感觉ClassUtils可以单独写一篇orz
ClassUtils 类在 spring-core 工程的 org.springframework.util 路径下。
简单来说,isPresent实际上是返回了ClassUtils.forName方法的处理结果,当forName方法正常执行,则鉴定的类被加载,返回true;若抛出异常(注意,此处异常是Throwable)则返回false。
forName方法的处理是:
首先,根据类名的长度(<=8)来确定是否是原始类型,若是原始类型则返回类对象Class<?>。
其次,判断是否是普通类型,若是原始类型则返回类对象Class<?>。
第三,判断是否数组类型,通过过滤"["字符截取类名,递归调用forName方法获取类对象,然后利用反射Array.newInstance创建对象并返回。
最后,排除以上情况后,使用classLoader来加载className指定的类。
补充
原始类型:Java的基本类型及其包装类,基本类型的数组类对象,以及空类型void.class。
普通类型:
最新文章
- [转载]Google Guava官方教程(中文版)
- REDHAT一总复习1更改系统文档文件
- android开发文档工具集(持续更新中...)
- web测试一般分为那几个阶段,哪些阶段是可以用工具实现的,都有些什么工具,哪些阶段必须要人工手动来实现呢?
- subprocess使用
- wikioi 1474 十进制转m进制
- 洛谷P1474 货币系统 Money Systems
- 【Binary Tree Level Order Traversal】cpp
- 【VMware虚拟化解决方案】设计和配置VMware vCenter 5.5
- Java生成缩略图Thumbnailator(转载)
- Mesos和kubernetes
- linux系统使用python监测网络接口获取网络的输入输出
- IOS(一) 基础控件的介绍以及使用
- c语言第五次作业0
- 基于Go的websocket消息服务
- (栈)leetcode856 Score of Parentheses
- xshell访问Ubuntu16.04显示乱码(即使在xshell设置了utf8)解决方案
- 【Android】LayoutInflater
- Executor 框架
- Thinkphp3.2.X自动生成应用目录
热门文章
- vue 2.5.14以上版本render函数不支持返回字符串
- [sql]join的5种方式:inner join、left(outer) join、right (outer) Join、full(outer) join、cross join
- thinkphp5中的配置如何使用
- WPF——RenderTransform特效
- StringUtils 正则校验
- zoj-3329-期望/dp/方程优化
- Java 调用 PHP 实例(五)
- 002——php字符串中的处理函数(一)
- checkbox选中的问题(Ajax.BeginForm)
- jstree 取消选中父节点