RestTemplate类的继承结构
- 通过idea的工具可以看到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有什么属性,也就是委托类,它的功能肯定要依靠这些属性来实现的。
先来看看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的构造方法可以看到,你可以使用默认的转换器,如下:
还可以使用自己准备的消息转换器,有这样的构造方法。可以去查看它的源码。 - 拿其中一个转换器来说明,
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
里使用,当然弄成私有类是合理的。静态内部类是嵌套类,它不能访问外围类的非静态成员,只能访问静态成员,除此之外,我觉得之所以要弄成静态类,是要说明这个内部类只是一个嵌套类,并不是外围类的一部分,关系不是很亲密。关于具体的区别可以参考这里。
- 看
接下来看看
ResponseErrorHandler
这个接口的功能,有两个方法hasError、handleError
,入参都是ClientHttpResponse
,spring自己定义的一个接口。
入参都是一个接口,这一层层的继承,肯定经过深思熟虑过的设计了,先跳过它。RestTemplate
使用的是默认的错误处理器,逻辑很简单,如果hasError
为真,就调用handleError
,处理的逻辑用户自己实现了,在这个默认的处理器中,就是抛出异常HttpClientErrorException
,这个异常又是一系列的接口继承:
要知道,每个接口展现的就是这个层面的功能,这样可以把一个大功能分成一个个独立的小功能,然后在具体的业务逻辑中去配对使用,也就是要什么功能就用哪个接口,同时又对其他的功能不了解,也不关心。这有点像tcp/ip协议的分层。先跳过,有空再来看看。
每次执行请求得到response后,就会使用这个错误处理器,先看看有没有错误,有的话就处理,没有就跳过。
看他怎么获得errorHandler
的,是通过一个获取方法,在effective java里有提到过这种方式,叫query method,在RestTemplate
里除了set、get外,没有别的地方会直接通过this.errorHandler
的方式来引用对象。估计在其他类也是一样。
处理错误后抛出异常就结束了,看来抛出的那个异常很重要啊,spring肯定会捕获到这个异常来展示一些信息。- 接下来就是看看
UriTemplateHandler
这个接口,RestTemplate
使用的也是默认的uri模板处理器,作用呢就是组装uri,有不同的组装方式,所以也就有不同的实现类。
UriTemplateHandler
在execute
这个比较底层的方法使用,也就是抽象出的步骤比较靠后。DefaultUriTemplateHandler
是使用UriComponentsBuilder
来扩展uri的。 接下来就是
RestTemplate
最主要的步骤了:
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>
这个类型的数据,如下图。
到这里呢,
RestTemplate
的实现介绍完了,接下来会介绍在springboot中它是怎么自动注入的,也好了解下springboot的自动配置。