近几年来,以信息为中心的表述性状态转移(Representational State Transfer,REST)已经称为替代传统SOAP Web 服务的流行方案。

REST与RPC几乎没有任何关系。RPC是面向服务的,并关注于行为和动作;而REST是面向资源的,强调描述应用程序的事物和名词。

RestTemplate定义了36个与REST资源交互的方法,其中的大多数都对应于HTTP的方法。 其实,这里面只有11个独立的方法,其中有十个有三种重载形式,而第十一个则重载了六次,这样一共形成了36个方法。

    • delete() 在特定的URL上对资源执行HTTP DELETE操作

    • exchange() 在URL上执行特定的HTTP方法,返回包含对象的ResponseEntity,这个对象是从响应体中映射得到的

    • execute() 在URL上执行特定的HTTP方法,返回一个从响应体映射得到的对象

    • getForEntity() 发送一个HTTP GET请求,返回的ResponseEntity包含了响应体所映射成的对象

    • getForObject() 发送一个HTTP GET请求,返回的请求体将映射为一个对象

    • postForEntity() POST 数据到一个URL,返回包含一个对象的ResponseEntity,这个对象是从响应体中映射得 到的

    • postForObject() POST 数据到一个URL,返回根据响应体匹配形成的对象

    • headForHeaders() 发送HTTP HEAD请求,返回包含特定资源URL的HTTP头

    • optionsForAllow() 发送HTTP OPTIONS请求,返回对特定URL的Allow头信息

    • postForLocation() POST 数据到一个URL,返回新创建资源的URL

    • put() PUT 资源到特定的URL

RestTemplate的核心之一 Http Client
目前通过RestTemplate 的源码可知,RestTemplate 可支持多种 Http Client的http的访问,如下所示:

  • 基于 JDK HttpURLConnection 的 SimpleClientHttpRequestFactory,默认。
  • 基于 Apache HttpComponents Client 的 HttpComponentsClientHttpRequestFactory
  • 基于 OkHttp3的OkHttpClientHttpRequestFactory。
  • 基于 Netty4 的 Netty4ClientHttpRequestFactory。

其中HttpURLConnection 和 HttpClient 为原生的网络访问类,OkHttp3采用了 OkHttp3的框架,Netty4 采用了Netty框架。

RestTemplate类的继承结构

通过IDE的工具可以看到RestTemplate的继承结构很简单:


由图可以看出继承了一个类,实现了一个接口,我们来看看这些接口和类有什么作用。
HttpAccessor:官方文档这样描述:Base class for RestTemplate and other HTTP accessing gateway helpers, defining common properties such as the ClientHttpRequestFactory to operate on.可以看出,这个抽象类不仅可以作为RestTemplate的基类,还可以让其他http访问网关助手去继承,然后就可以获得它的功能了,同时呢,这个抽象类是设置一些共有的属性,其中ClientHttpRequestFactory就是在这里设置的。在我看来,这种抽象类是在避免样板代码,把共有的代码放到上层,但是如果要设置其他共有属性呢,只能一层层继承下去了。比如接下来要看的InterceptingHttpAccessor。
InterceptingHttpAccessor:Base class for RestTemplate and other HTTP accessing gateway helpers, adding interceptor-related properties to HttpAccessor's common properties.就是加了一系列拦截器,这些拦截器会在请求执行之前执行,比如添加一些请求头之类的,http Basic认证就可以通过这种方式来加上去,spring提供这个类BasicAuthorizationInterceptor。至于这个接口为什么给定义的方法ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)三个这样的参数,还得去想想。
RestOperations:Interface specifying a basic set of RESTful operations. Implemented by RestTemplate. Not often used directly, but a useful option to enhance testability, as it can easily be mocked or stubbed.这个接口是和RESTful api配套的,指定RESTful基本操作集。也就是说这个接口说明了一种能力,一种可以发各种rest请求的能力。

从上面的分析来看呢,RestTemplate类继承了可以设置共有属性的类,有了基础的http请求功能,然后实现了RESTful基本操作集的接口,有了发rest请求的能力,当然这个能力得RestTemplate它自己去利用这些共有属性去实现了。这么一组合呢,RestTemplate就可以拿来用了。当然,http协议是比较复杂的,各种请求头啊、不同的数据格式啊等等,这些问题的处理逻辑当然不会放在RestTemplate这个类中,不然会写的很长。所以就有了很多委托类,为了让用户有自己的扩展,这些委托类一般都采用了策略模式来设计,其实就是多态。接下来我们就来看看这些委托类以及它们的设计思想。
先大概看看RestTemplate有什么属性,也就是委托类,它的功能肯定要依靠这些属性来实现的。

1、先来看看messageConverters消息转换器,这是干嘛用的呢?

  • 看HttpMessageConverter<T>这个接口的描述Strategy interface that specifies a converter that can convert from and to HTTP requests and responses.可以看到这是一个策略接口,策略模式见《行为类模式--策略模式》然后就是说它可以从request和response转换数据,也就是T类型和request支持的mime(多功能网络邮件扩展协议)类型数据互相转换。比如User这个类转换成json格式,也可以把json格式的User转换成User类,当然还有其他的mime类型。
  • 从RestTemplate的构造方法可以看到,你可以使用默认的转换器,如下:
public RestTemplate() {
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(new StringHttpMessageConverter());
this.messageConverters.add(new ResourceHttpMessageConverter());
this.messageConverters.add(new SourceHttpMessageConverter<Source>());
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());
}
}

还可以使用自己准备的消息转换器,有这样的构造方法。可以去查看它的源码。

  • 拿其中一个转换器来说明,AllEncompassingFormHttpMessageConverter这个转换器继承了FormHttpMessageConverter,FormHttpMessageConverter的描述太长了,可以去官方文档查看https://docs.spring.io/spring/docs/4.3.17.BUILD-SNAPSHOT/javadoc-api/,它可以读写常规的表单类型,也就是application/x-www-form-urlencoded,但是只能写multipart类型数据,不能读,那如果要读的话是用哪个转换器呢?还得去看看。有意思的是,描述的最后一句话,Some methods in this class were inspired by org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity.说明spring还是参考了apache在这方面的实现,刚好我最近做的一个api测试工具就是用到apache的MultipartEntityBuilder来构建httpEntity。

  从它的属性和构造方法可知,每个part可以是不同的数据类型,然后有多个partConverter来解析这些数据。同时呢,它所支持的mime类型也初始化了,然后在实现HttpMessageConverter<T>的canRead、canWrite方法时就能用到了。接下来看看read和write方法,它们是怎么实现的,我们可以从实现的角度来看这两个方法的参数设置有什么学问,如果是自己来设计的话,会是什么样呢?

  可以从它的代码看到,首先从HttpInputMessage获得一个流,为什么是这个接口,这个接口有什么用,还有一个HttpOutputMessage,这两个有什么区别,还得思考思考,暂且跳过这个问题,接下来就是得到一个字符串了,然后通过“&”这个符号分割,放到MultiMap里返回出去了。clazz这个参数没有用到,在别的转换器有用的,但是还不知道有什么用。总的看来,这是读常规表单的数据,并没有读multipart类型的,也没有这样类似的read方法了。

  write的话从代码可以看出能写两种格式,常规表单和multipart,具体的逻辑可查看它的源码。

我们可以看到FormHttpMessageConverter这个类里还有一个私有的静态类,只能内部使用,是用来写一个mime类型的数据。multipart类型有多个不同类型的part,写每个part到OutputStream时都要用到一个转换器,而这个转换器需要一个HttpOutputMessage实现类,并不是一个OutputStream,所以封装一下,然后通过HttpOutputMessage的方法(也是入口)来改变OutputStream。
这里有个问题,为什么是静态类。私有类还好理解,这个类只在FormHttpMessageConverter里使用,当然弄成私有类是合理的。静态内部类是嵌套类,它不能访问外围类的非静态成员,只能访问静态成员,除此之外,我觉得之所以要弄成静态类,是要说明这个内部类只是一个嵌套类,并不是外围类的一部分,关系不是很亲密。关于具体的区别可以参考这里。

2、接下来看看ResponseErrorHandler这个接口的功能,有两个方法hasError、handleError,入参都是ClientHttpResponse,spring自己定义的一个接口。

入参都是一个接口,这一层层的继承,肯定经过深思熟虑过的设计了,先跳过它。RestTemplate使用的是默认的错误处理器,逻辑很简单,如果hasError为真,就调用handleError,处理的逻辑用户自己实现了,在这个默认的处理器中,就是抛出异常HttpClientErrorException,这个异常又是一系列的接口继承:

要知道,每个接口展现的就是这个层面的功能,这样可以把一个大功能分成一个个独立的小功能,然后在具体的业务逻辑中去配对使用,也就是要什么功能就用哪个接口,同时又对其他的功能不了解,也不关心。这有点像tcp/ip协议的分层。先跳过,有空再来看看。
每次执行请求得到response后,就会使用这个错误处理器,先看看有没有错误,有的话就处理,没有就跳过。

看他怎么获得errorHandler的,是通过一个获取方法,在effective java里有提到过这种方式,叫query method,在RestTemplate里除了set、get外,没有别的地方会直接通过this.errorHandler的方式来引用对象。估计在其他类也是一样。
处理错误后抛出异常就结束了,看来抛出的那个异常很重要啊,spring肯定会捕获到这个异常来展示一些信息。

3、接下来就是看看UriTemplateHandler这个接口,RestTemplate使用的也是默认的uri模板处理器,作用呢就是组装uri,有不同的组装方式,所以也就有不同的实现类。

UriTemplateHandler在execute这个比较底层的方法使用,也就是抽象出的步骤比较靠后。DefaultUriTemplateHandler是使用UriComponentsBuilder来扩展uri的。

3、接下来就是RestTemplate最主要的步骤了:

例如上面是restTemplate源码中的getForObject和getForObject的 调用

RestTemplate的主要逻辑就是这三步了,第一和第三步分别交给了RequestCallback、ResponseExtractor,来看看是怎么实现并使用这两个接口的。
首先是RequestCallback,官方描述Callback interface for code that operates on a ClientHttpRequest. Allows to manipulate the request headers, and write to the request body.
Used internally by the RestTemplate, but also useful for application code.,它是个回调接口,而且是对ClientHttpRequest操作的,方法就一个doWithRequest, ClientHttpRequest作为它的输出参数,很简单。然后它的实现也很简单,而且实现都在RestTemplate里,还是私有类,这个接口看来是个小众接口,使用范围不是很广。具体的实现可以查看源码。
然后瞧瞧ResponseExtractor这个接口,官方描述Generic callback interface used by RestTemplate's retrieval methods Implementations of this interface perform the actual work of extracting data from a ClientHttpResponse, but don't need to worry about exception handling or closing resources.Used internally by the RestTemplate, but also useful for application code.也是一个普通的回调接口,而且不用在实现这个方法的时候担心异常处理和资源关闭的问题,因为这个方法是抛出异常的。
它有三个实现类,一个是从response里抽取headers,一个是抽取httpEntity,另一个就有意思了,

这个实现类封装了HttpMessageConverterExtractor,相当于做了一个代理,目的是获得ResponseEntity<T>这个类型的数据,如下图。

4、到这里呢,RestTemplate的实现介绍完了,接下来会介绍在springboot中它是怎么自动注入的,也好了解下springboot的自动配置。

TabHost Tab的添加和删除的更多相关文章

  1. tab一些 添加 删除 搜索

    tab一些 添加 删除 搜索 案例 <!DOCTYPE html><html lang="en"><head> <meta charset ...

  2. easyui学习笔记12—tab标签页的添加和删除

    这一篇我们来看看标签页的添加和删除动作.我在想看这些例子还不如看文档,文档的内容更加全面,但是文档全部是理论没有实际的操作,看起来很枯燥,文档只能是遇到问题的时候查.easyui的文档写的还是很详细的 ...

  3. 仅在TabControl中的Tab中添加右键菜单

    若想实现仅在TabControl中的Tab中添加右键菜单,可在XAML中通过使用样式得到: <TabControl> <TabControl.ItemContainerStyle&g ...

  4. 用JS添加和删除class类名

    下面介绍一下如何给一个节点添加和删除class名 添加:节点.classList.add("类名"): 删除:节点.classList.remove("类名") ...

  5. Android零基础入门第87节:Fragment添加、删除、替换

    前面一起学习了Fragment的创建和加载,以及其生命周期方法,那么接下来进一步来学习Fragment的具体使用,本期先来学习Fragment添加.删除.替换. 一.概述 在前面的学习中,特别是动态加 ...

  6. 基于JavaScript的表格设计:按序添加或删除班级的学生信息

    目的: 制作一个表格,显示班级的学生信息 功能: 鼠标移到不同行,背景色发生改变,离开恢复原背景色 添加.删除按钮,可添加,可删除. 程序流程: 首先先建立绑定事件函数. 其次建立鼠标移动改变背景色函 ...

  7. MVC5 网站开发之六 管理员 2、添加、删除、重置密码、修改密码、列表浏览

    目录 奔跑吧,代码小哥! MVC5网站开发之一 总体概述 MVC5 网站开发之二 创建项目 MVC5 网站开发之三 数据存储层功能实现 MVC5 网站开发之四 业务逻辑层的架构和基本功能 MVC5 网 ...

  8. [vs2008]Visual Studio 2008 SP1添加或删除功能提示查找SQLSysClrTypes.msi文件

    前言 今天接到领导布置的一个任务,是之前同事负责的项目.离职了,现在客户有些地方需要修改,由于我之前参与过,就落在我的头上了. 然后我就把代码弄了过来,打开发现其中需要用到水晶报表.(我觉得不好用,不 ...

  9. JavaWeb_day03_员工信息添加修改删除

    day03员工的添加,修改,删除 修改功能 思路 : 点击修改员工数据之后,跳转到单行文本,查询要修改的员工id的全部信息,主键id设置为readonly,其余的都可以修改, 修改之后,提交按钮,提交 ...

随机推荐

  1. Android Activity生命周期 onSaveInstanceState和onRestoreInstanceState

    当某个activity变得“容易”被系统销毁时,该activity的onSaveInstanceState就会被执行,除非该activity是被用户主动销毁的,例如当用户按BACK键的时候. 注意上面 ...

  2. ASP.Net WebForm学习笔记:一、aspx与服务器控件探秘

    作者:周旭龙 出处:http://edisonchou.cnblogs.com 开篇:毫无疑问,ASP.Net WebForm是微软推出的一个跨时代的Web开发模式,它将WinForm开发模式的快捷便 ...

  3. MySQL 建表字段长度的限制

    脑补,varchar(N),N指的是最大字符数,不是字节数. 先上测试说明:   在MySQL建表时,遇到一个奇怪的现象: root@localhost : test 10:30:54>CREA ...

  4. HTML5 增强的页面元素

    一.HTML5 改良的 input 元素的种类 1.<input type="number" id="num1"> var n1 = documen ...

  5. 2014--9=17 软工二班 MyEclipse blue==修改浏览器语言

  6. 枚举桌面应用程序lnk路径并得到对应程序的绝对路径(使用SHGetDesktopFolder函数枚举,然后使用COM查询IShellFolder,IShellLink和IPersistFile接口)

    // 枚举桌面上应用程序的lnk路径 void EnumDesktopLnkPath() { WCHAR szPath[MAX_PATH] = {0}; SHGetSpecialFolderPathW ...

  7. Android:利用SharedPreferences实现自动登录

    主要代码: public class LoginActivity extends Activity { private EditText username; private EditText user ...

  8. renameTo()方法的用法

    使用renameTo()方法,可以将文件data.txt从C:\JavaApp\IOTest1\目录移动到C:\目录,并改名为newdata.txt import java.io.File; //将文 ...

  9. Android 线程通讯类Handler

    handler是线程通讯工具类.用于传递消息.它有两个队列: 1.消息队列 2.线程队列 消息队列使用sendMessage和HandleMessage的组合来发送和处理消息. 线程队列类似一段代码, ...

  10. Android LayoutInflater.inflate()的参数及其用法

    很多人在网上问LayoutInflater类的用法,以及inflate()方法参数的含义,现解释如下: inflate()的作用就是将一个用xml定义的布局文件查找出来,注意与findViewById ...