近几年来,以信息为中心的表述性状态转移(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的自动配置。

使用spring-boot-starter-amqp开发生产者应用的更多相关文章

  1. 从零开始开发一个Spring Boot Starter

    一.Spring Boot Starter简介 Starter是Spring Boot中的一个非常重要的概念,Starter相当于模块,它能将模块所需的依赖整合起来并对模块内的Bean根据环境( 条件 ...

  2. 最详细的自定义Spring Boot Starter开发教程

    1. 前言 随着Spring的日渐臃肿,为了简化配置.开箱即用.快速集成,Spring Boot 横空出世. 目前已经成为 Java 目前最火热的框架了.平常我们用Spring Boot开发web应用 ...

  3. Spring Boot Starter 开发指南

    Spring Boot Starter是什么? 依赖管理是任何复杂项目的关键部分.以手动的方式来实现依赖管理不太现实,你得花更多时间,同时你在项目的其他重要方面能付出的时间就会变得越少. Spring ...

  4. SpringBoot 之Spring Boot Starter依赖包及作用

    Spring Boot 之Spring Boot Starter依赖包及作用 spring-boot-starter 这是Spring Boot的核心启动器,包含了自动配置.日志和YAML. spri ...

  5. Spring Boot Starter列表

    转自:http://blog.sina.com.cn/s/blog_798f713f0102wiy5.html Spring Boot Starter 基本的一共有43种,具体如下: 1)spring ...

  6. Spring Boot Starter 介绍

    http://www.baeldung.com/spring-boot-starters 作者:baeldung 译者:http://oopsguy.com 1.概述 依赖管理是任何复杂项目的关键部分 ...

  7. spring -boot s-tarter 详解

    Starter POMs是可以包含到应用中的一个方便的依赖关系描述符集合.你可以获取所有Spring及相关技术的一站式服务,而不需要翻阅示例代码,拷贝粘贴大量的依赖描述符.例如,如果你想使用Sprin ...

  8. 手把手教你定制标准Spring Boot starter,真的很清晰

    写在前面 我们每次构建一个 Spring 应用程序时,我们都不希望从头开始实现具有「横切关注点」的内容:相反,我们希望一次性实现这些功能,并根据需要将它们包含到任何我们要构建的应用程序中 横切关注点 ...

  9. 年轻人的第一个自定义 Spring Boot Starter!

    陆陆续续,零零散散,栈长已经写了几十篇 Spring Boot 系列文章了,其中有介绍到 Spring Boot Starters 启动器,使用的.介绍的都是第三方的 Starters ,那如何开发一 ...

  10. 一个简单易上手的短信服务Spring Boot Starter

    前言 短信服务在用户注册.登录.找回密码等相关操作中,可以让用户使用更加便捷,越来越多的公司都采用短信验证的方式让用户进行操作,从而提高用户的实用性. Spring Boot Starter 由于 S ...

随机推荐

  1. [RK3288][Android6.0] 调试笔记 --- user版本默认显示开发者选项【转】

    本文转载自:https://blog.csdn.net/kris_fei/article/details/70157137 Platform: ROCKCHIPOS: Android 6.0Kerne ...

  2. linux 安装mysql服务

    1.检查是否已安装,grep的-i选项表示匹配时忽略大小写 rpm -qa|grep -i mysql *可见已经安装了库文件,应该先卸载,不然会出现覆盖错误.注意卸:载时使用了--nodeps选项, ...

  3. 使用 Apache Spark 让 MySQL 查询速度提升 10 倍以上

    转: https://coyee.com/article/11012-how-apache-spark-makes-your-slow-mysql-queries-10x-faster-or-more ...

  4. win 7 processing 编程环境搭建

    1.下载processing安装包: 2.下载usb驱动: 3.安装processing; 4.安装驱动: 5.在processing中编写代码: // Visualizing the data fr ...

  5. socket io 记得flush

    public class Client { public static void main(String args[]) throws Exception { //为了简单起见,所有的异常都直接往外抛 ...

  6. ajax01简介

    (Asynchronous JavaScript and XML)Ajax :异步 JavaScript 和 XML,一种允许浏览器和服务器通信进行少量数据交换而无需重新加载整个网页,以实现更新部分网 ...

  7. 爬虫第三篇:requests模块

    requests模块其实就是对urllib.request模块的进步一不优化,提供了很多可选的参数,同时简化了操作.下面我还是贴上具体操作的代码. requests GET请求 GET请求html文件 ...

  8. java常见设计模式

    工厂模式 普通工厂模式,就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建. 多个工厂模式,编写多个创建工厂的方法即可. 静态工厂模式,在多个工厂模式的基础上把Factory种方法的返回值标明 ...

  9. HTML5 拖放---drag和drop

    拖放四步走:第一步:设置元素可拖放,即把 draggable属性设置为 true:  例:<div id="div" draggable="true"&g ...

  10. ScrollView垂直滚动控件

    ScrollView垂直滚动控件 一.简介 二.方法 1)ScrollView垂直滚动控件使用方法 1.在layout布局文件的最外层建立一个ScrollView控件 2.在ScrollView控件中 ...