近几年来,以信息为中心的表述性状态转移(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. vRO Extend VirtualDisk Workflow

    https://vbombarded.wordpress.com/2015/02/20/vrealize-orchestrator-extend-virtual-disk-workflow/ var ...

  2. 实现HTML格式的数据报表邮件

  3. 使用eclipse搭建第一个python+Django的web开发实例

    python+Django的web开发实例   一.创建一个项目如果这是你第一次使用Django,那么你必须进行一些初始设置.也就是通过自动生成代码来建立一个Django项目--一个Django项目的 ...

  4. springmvc-restful

    1.restful概述 REST 仅仅是一种架构的风格,并不是真正的架构,也不是一个软件,而是一种思想. 我们可以基于现有的HTTP.URI.XML.等现有技术来实现REST的风格.而不用去学习任何新 ...

  5. HMM简单理解(来自quora&其他网上资料)

    转载自quora: 连接:https://www.quora.com/What-is-a-simple-explanation-of-the-Hidden-Markov-Model-algorithm ...

  6. java.sql.SQLException: Incorrect string value: '\xF0\x9F\x91\x88\xE6\x88...' for column 'content' at row 1

    往MySQL插入数据时,报错如下 java.sql.SQLException: Incorrect at com.mysql.cj.jdbc.exceptions.SQLError.createSQL ...

  7. 分开统计的sql写法

    DECLARE @StartDate DATETIME= '2017-10-13 00:00:00';DECLARE @EndDate DATETIME= '2017-11-13 23:00:00'; ...

  8. openstack live migration性能分析

    http://blog.zhaw.ch/icclab/an-analysis-of-the-performance-of-live-migration-in-openstack/ http://blo ...

  9. Linux下DB2的TCP配置

    1切换到db2实例用户下(uppdb) su - uppdb 2查看db2是否开启tcp服务 db2set -all 设置tcp服务:db2set DB2COMM=TCPIP 3查看是否配置db2 t ...

  10. php 策略模式案例

    策略模式,将一组特定的行为和算法封装成类,以适应某些特定的上下文环境. eg:假如有一个电商网站系统,针对男性女性用户要各自跳转到不同的商品类目,并且所有的广告位展示不同的广告.在传统的代码中,都是在 ...