restTemplate源码解析(一)构造restTemplate的Bean实例
所有文章
https://www.cnblogs.com/lay2017/p/11740855.html
正文
构造一个restTemplate的Bean实例很容易,只需这样配置
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
但我们希望更多得了解一个这个构造过程做了哪些事情。为此,我们需要去打开restTemplate这个黑盒子。
restTemplate设计
先试着从restTemplate得类图上获得一些灵感,看看spring的作者是一个什么样的设计思路吧。

我们看到,简单的几个类和接口展示了标准的抽象设计。
HttpAccessor是一个抽象类,它抽象的是一个http访问器的概念。这说明,RestTemplate具备http方式请求响应的处理能力。再看RestOperations,它是一个接口,抽象的是restful风格的操作方法,这意味着RestTemplate的操作风格将是Restful的。
http访问器和restful风格操作做了一个分离。restTemplate则是聚合了两者,既有http访问器的基础能力,又在这个基础之上构建了restful风格。设计者把Restful操作作为接口,其实从规范角度上,我们应该面向RestOperations接口使用它。但大多数人似乎都直接使用RestTemplate,我们上面的代码示例也无意识地直接面向了RestTemplate这个实现类。
restTemplate实例构造过程
接下来,我们再看看new一个RestTemplate对象做了哪些事情。
进入RestTemplate类的构造方法
private final List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
private UriTemplateHandler uriTemplateHandler;
public RestTemplate() {
// 添加HttpMessageConverterjie
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(new StringHttpMessageConverter());
this.messageConverters.add(new ResourceHttpMessageConverter(false));
try {
this.messageConverters.add(new SourceHttpMessageConverter<>());
}
catch (Error err) {
// Ignore when no TransformerFactory implementation is available
}
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
if (romePresent) {
this.messageConverters.add(new AtomFeedHttpMessageConverter());
this.messageConverters.add(new RssChannelHttpMessageConverter());
}
if (jackson2XmlPresent) {
this.messageConverters.add(new MappingJackson2XmlHttpMessageConverter());
}
else if (jaxb2Present) {
this.messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
}
if (jackson2Present) {
this.messageConverters.add(new MappingJackson2HttpMessageConverter());
}
else if (gsonPresent) {
this.messageConverters.add(new GsonHttpMessageConverter());
}
else if (jsonbPresent) {
this.messageConverters.add(new JsonbHttpMessageConverter());
}
if (jackson2SmilePresent) {
this.messageConverters.add(new MappingJackson2SmileHttpMessageConverter());
}
if (jackson2CborPresent) {
this.messageConverters.add(new MappingJackson2CborHttpMessageConverter());
}
// uri模板处理器
this.uriTemplateHandler = initUriTemplateHandler();
}
构造方法有点长,但只做了两件事
1)添加HttpMessageConverter的实现类,熟悉springmvc的话估计知道HttpMessageConverter。顾名思义,它就是用来转换http请求响应过程中的消息数据的。
2)初始化一个UriTemplateHandler
我们跟进initUriTemplateHandler方法,看看怎么初始化一个uri模板处理器的
private static DefaultUriBuilderFactory initUriTemplateHandler() {
DefaultUriBuilderFactory uriFactory = new DefaultUriBuilderFactory();
uriFactory.setEncodingMode(EncodingMode.URI_COMPONENT); // for backwards compatibility..
return uriFactory;
}
这里只是简单地构造了实例,并直接返回。由此可见,DefaultUriBuilderFactory应该直接或者间接实现了UriTemplateHandler
我们看看DefaultUriBuilderFactory的类图

与我们猜想的一样,DefaultUriBuilderFactory间接实现了UriTemplateHandler
RestTemplate的构造方法中就做了这两件事,我们再看看HttpAccessor
public abstract class HttpAccessor {
// ...
private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
// ...
}
默认创建了一个ClientHttpRequestFactory的实例,ClientHttpRequestFactory这个接口是用来创建客户端的request请求对象的,我们并没有去自定义设置它默认采用SimpleClientHttpRequestFactory这个实现类。
ClientHttpRequestFactory作为工厂模式,将会生产ClientHttpRequest。ClientHttpRequest抽象了Http请求操作,执行ClientHttpRequest将会发送请求与服务端交互。
总结
到这里,RestTemplate的实例构造过程就结束了。其实它的原理就是封装了http请求操作而已。
我们再大体过一下RestTemplate的构造过程
1)在RestTemplate构造方法中添加了多个HttpMessageConverter后续用于http请求响应的数据转换
2)在RestTemplate构造方法中初始化了一个Uri模板的处理器,后续用于处理uri相关的东西
3)HttpAccessor默认创建了一个ClientHttpRequestFactory的成员实例,后续用于创建请求对象。
不过,似乎最有学习价值的还是一开始的restTemplate的设计图。能够理解抽象类与接口的标准设计理念,兴许对面向对象分析与设计大有裨益。
restTemplate源码解析(一)构造restTemplate的Bean实例的更多相关文章
- 多线程爬坑之路-Thread和Runable源码解析之基本方法的运用实例
前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...
- [Java多线程]-Thread和Runable源码解析之基本方法的运用实例
前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...
- restTemplate源码解析(目录)
restTemplate是spring实现的,基于restful风格的http请求模板.使用restTemplate可以简化请求操作的复杂性,同时规范了代码风格.本系列文章,将根据以下目录顺序,从源码 ...
- restTemplate源码解析(五)处理ClientHttpResponse响应对象
所有文章 https://www.cnblogs.com/lay2017/p/11740855.html 正文 上一篇文章中,我们执行了ClientHttpRequest与服务端进行交互.并返回了一个 ...
- restTemplate源码解析(二)restTemplate的核心逻辑
所有文章 https://www.cnblogs.com/lay2017/p/11740855.html 正文 上一篇文章中,我们构造了一个RestTemplate的Bean实例对象.本文将主要了解一 ...
- restTemplate源码解析(三)创建ClientHttpRequest请求对象
所有文章 https://www.cnblogs.com/lay2017/p/11740855.html 正文 上一篇文章中,我们大体看了一下restTemplate的核心逻辑.再回顾一下核心代码 p ...
- restTemplate源码解析(四)执行ClientHttpRequest请求对象
所有文章 https://www.cnblogs.com/lay2017/p/11740855.html 正文 上一篇文章中,我们创建了一个ClientHttpRequest的实例.本文将继续阅读Cl ...
- springcloud源码解析(目录)
springcloud是一个基于springboot的一站式企业级分布式应用开发框架.springboot为其提供了创建单一项目的便利性,springcloud组合了现有的.常用的分布式项目的解决方案 ...
- Spring AOP的实现及源码解析
在介绍AOP之前,想必很多人都听说AOP是基于动态代理和反射来实现的,那么在看AOP之前,你需要弄懂什么是动态代理和反射及它们又是如何实现的. 想了解JDK的动态代理及反射的实现和源码分析,请参见下面 ...
随机推荐
- JDK目录详解
bin目录: 该目录用于存放一些可执行程序. 如javac.exe(java编译器).java.exe(java运行工具),jar.exe(打包工具)和* javadoc.exe(文档生成工具)等. ...
- lua日期处理函数
function day_step(old_day,step) local y,m,d if("0" ~= string.sub(old_day,6,6)) then ...
- Tips for Conda
管理环境 创建环境 基于 python3.6 创建一个名为test_py3的环境 conda create -n test_py3 python=3.6 基于 python2.7 创建一个名为test ...
- svn如何撤销之前某个版本所做的改变
撤销这个版本所做的修改:(撤销这个版本所做的修改) 右键项目svn->show log->revert changes from this revision 如果要恢复到某个版本:(这个版 ...
- 【C# 开发技巧】在VS程序如何取消.vshost的进程
我们在双击执行一个EXE程序,会有两个进程,程序关闭之后,貌似只能关闭你的程序,附加的vshost.exe仍然存在.如下图 解决方案: 1.在调试页面,改成release,同时取消最后一项启用承载进程 ...
- 【c# 学习笔记】使用新成员隐藏基类成员
如果想在派生类中定义与基类成员同名的成员,则可以使用new关键字把基类成员隐藏起来. 如果不适应new关键字,在派生类中定义一个与基类成员同名的成员,编译器将产生警告信息,如下代码演示: public ...
- 图形学入门(1)——直线生成算法(DDA和Bresenham)
开一个新坑,记录从零开始学习图形学的过程,现在还是个正在学习的萌新,写的不好请见谅. 首先从最基础的直线生成算法开始,当我们要在屏幕上画一条直线时,由于屏幕由一个个像素组成,所以实际上计算机显示的直线 ...
- windows下进程与线程
windows下进程与线程 Windows是一个单用户多任务的操作系统,同一时间可有多个进程在执行.进程是应用程序的运行实例,可以理解为应用程序的一次动态执行:而线程是CPU调度的单位,是进程的一个执 ...
- MySQL 全局锁和表锁
根据加锁的范围,MySQL 里面的锁大致可以分成全局锁,表级锁,行锁. 行锁已经在前面几篇文章说过 1. 全局锁 全局锁就是对整个数据库实例加锁.MySQL 提供了一个加全局读锁的方法,命令是Flus ...
- python 计算文件夹里所有内容的大小总和
计算文件夹里所有内容的大小总和 递归方法 '''计算文件夹的大小''' import os def dir_file_size(path): if os.path.isdir(path): file_ ...