相信RestTemplate是我们在SpringBoot中用过的最多的http客户端,今天来分析他的源码。
一、最简单的用法
首先需要向容器中注入RestTemplate的Bean实例
-
@Configuration
-
public
class
RestTemplateConfig {
-
-
@Bean
-
public RestTemplate
getRestTemplate
() {
-
return
new
RestTemplate();
-
}
-
-
}
接着直接使用即可
-
@RestController
-
public
class
Controller {
-
-
@Autowired
-
RestTemplate restTemplate;
-
-
@RequestMapping("/test")
-
public String
test
() {
-
String
result
= restTemplate.getForObject(
"https://www.baidu.com/", String.class);
-
System.out.println(result);
-
return result;
-
}
-
}
可以看到,RestTemplate简化了http请求操作,甚至只需要传入一个url与响应的类型,即可获取结果。
二、RestTemplate类图结构
类图结构是我们直观认识一个类最有效的工具,很多人可能都不知道怎么看类图。
只需要简单的三步

那么他的类图结构是这样子的:

RestOperations是一个接口,意为Rest操作,抽象出了具有restful风格的操作方法,我们常用的getForObject、postForEntity、exchange都是里面定义的接口方法。
如图:

HttpAccessor则是一个抽象类,意为Http访问器,内部保存了请求的工厂类,即ClientHttpRequestFactory类,使用createRequest生产ClientHttpRequest,RestTemplate最终会将请求构造成ClientHttpRequest,由ClientHttpRequest负责与服务端进行交互。先记住这个类,后面将会用到。
其中createRequest方法如下
-
protected ClientHttpRequest
createRequest
(URI url, HttpMethod method)
throws IOException {
-
//this.getRequestFactory()返回默认的SimpleClientHttpRequestFactory
-
ClientHttpRequest
request
=
this.getRequestFactory().createRequest(url, method);
-
this.initialize(request);
-
if (
this.logger.isDebugEnabled()) {
-
this.logger.debug(
"HTTP " + method.name() +
" " + url);
-
}
-
-
return request;
-
}
InterceptingHttpAccessor是HttpAccessor的实现类,主要是增加了一些有关拦截器的逻辑,这个本文就不再赘述。
三、RestTemplate的构造方法
看源码之前,大家可以先去下载Spring框架的源码,鉴于github速度感人,可以去码云上下载https://gitee.com/mirrors/Spring-Framework自己感兴趣的版本,记得先安装好Gradle环境哦,Gradle的环境安装与配置,当然idea也有自带的Gradle。
无参构造方法如下:
-
public
RestTemplate
() {
-
this.messageConverters.add(
new
ByteArrayHttpMessageConverter());
-
this.messageConverters.add(
new
StringHttpMessageConverter());
-
this.messageConverters.add(
new
ResourceHttpMessageConverter(
false));
-
if (!shouldIgnoreXml) {
-
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 (!shouldIgnoreXml) {
-
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());
-
}
-
else
if (kotlinSerializationJsonPresent) {
-
this.messageConverters.add(
new
KotlinSerializationJsonHttpMessageConverter());
-
}
-
-
if (jackson2SmilePresent) {
-
this.messageConverters.add(
new
MappingJackson2SmileHttpMessageConverter());
-
}
-
if (jackson2CborPresent) {
-
this.messageConverters.add(
new
MappingJackson2CborHttpMessageConverter());
-
}
-
-
this.uriTemplateHandler = initUriTemplateHandler();
-
}
构造方法主要做了两件事:
添加了很多HttpMessageConverter的实现类,HttpMessageConverter的各个实现类主要是将请求响应的文本转化成相应的java对象,想要了解更多关于HttpMessageConverter的信息,可以参考这篇文章HttpMessageConverter那回事
初始化了uri模版处理器,这个处理器主要对uri做一些拼接操作,例如在get请求中,将param拼接到url的后面。
四、执行请求的核心逻辑
我们以一开始的代码为例,逐步进入到核心逻辑中。
restTemplate.getForObject("https://www.baidu.com/", String.class)
从上面中可以了解到,getForObject是RestOperation中的方法,RestTemplate的实现为:
-
public <T> T
getForObject
(String url, Class<T> responseType, Object... uriVariables)
throws RestClientException {
-
//获取请求回调
-
RequestCallback
requestCallback
= acceptHeaderRequestCallback(responseType);
-
//获取http消息转化抽取器
-
HttpMessageConverterExtractor<T> responseExtractor =
-
new
HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
-
return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
-
}
走进 acceptHeaderRequestCallback(responseType)
-
public <T> RequestCallback
acceptHeaderRequestCallback
(Class<T> responseType) {
-
return
new
AcceptHeaderRequestCallback(responseType);
-
}
返回了AcceptHeaderRequestCallback实例,这个对象的doWithRequest会在后面用到,先放着。
HttpMessageConverterExtractor实现了ResponseExtractor,可以将http响应的文本数据转化成相应的java对象。
接着进入execute方法中
-
public <T> T
execute
(String url, HttpMethod method, @Nullable RequestCallback requestCallback,
-
@Nullable ResponseExtractor<T> responseExtractor, Object... uriVariables)
throws RestClientException {
-
//拼接uri
-
URI
expanded
= getUriTemplateHandler().expand(url, uriVariables);
-
return doExecute(expanded, method, requestCallback, responseExtractor);
-
}
这个很简单,直接进入doExecute中,该方法就是RestTemplate的主流程代码,比较核心。
-
protected <T> T
doExecute
(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
-
@Nullable ResponseExtractor<T> responseExtractor)
throws RestClientException {
-
-
Assert.notNull(url,
"URI is required");
-
Assert.notNull(method,
"HttpMethod is required");
-
ClientHttpResponse
response
=
null;
-
try {
-
//创建文章开头所说的ClientHttpRequest
-
ClientHttpRequest
request
= createRequest(url, method);
-
if (requestCallback !=
null) {
-
//执行请求回调
-
requestCallback.doWithRequest(request);
-
}
-
//执行请求,U获取响应结果
-
response = request.execute();
-
//处理响应结果
-
handleResponse(url, method, response);
-
//利用响应抽取器抽取data返回预先定义的java对象,例如例子中的String
-
return (responseExtractor !=
null ? responseExtractor.extractData(response) :
null);
-
}
-
catch (IOException ex) {
-
String
resource
= url.toString();
-
String
query
= url.getRawQuery();
-
resource = (query !=
null ? resource.substring(
0, resource.indexOf(
'?')) : resource);
-
throw
new
ResourceAccessException(
"I/O error on " + method.name() +
-
" request for \"" + resource +
"\": " + ex.getMessage(), ex);
-
}
-
finally {
-
if (response !=
null) {
-
response.close();
-
}
-
}
-
}
其中涉及到多个方法:
createRequest(url, method)
此处调用的是HttpAccessor中的createRequest方法
-
protected ClientHttpRequest
createRequest
(URI url, HttpMethod method)
throws IOException {
-
//this.getRequestFactory()返回默认的SimpleClientHttpRequestFactory
-
ClientHttpRequest
request
=
this.getRequestFactory().createRequest(url, method);
-
this.initialize(request);
-
if (
this.logger.isDebugEnabled()) {
-
this.logger.debug(
"HTTP " + method.name() +
" " + url);
-
}
-
-
return request;
-
}
其中getRequestFactory方法被InterceptingHttpAccessor(在类图结构中有该类)重写了
-
public ClientHttpRequestFactory
getRequestFactory
() {
-
List<ClientHttpRequestInterceptor> interceptors = getInterceptors();
-
if (!CollectionUtils.isEmpty(interceptors)) {
-
ClientHttpRequestFactory
factory
=
this.interceptingRequestFactory;
-
if (factory ==
null) {
-
factory =
new
InterceptingClientHttpRequestFactory(
super.getRequestFactory(), interceptors);
-
this.interceptingRequestFactory = factory;
-
}
-
return factory;
-
}
-
else {
-
return
super.getRequestFactory();
-
}
-
}
可以看到,一上来先获取拦截器,不过我们并没有设置拦截器。因此直接返回了父类即HttpAccessor中的RequestFactory
-
public
abstract
class
HttpAccessor {
-
-
/** Logger available to subclasses. */
-
protected
final
Log
logger
= HttpLogging.forLogName(getClass());
-
-
private
ClientHttpRequestFactory
requestFactory
=
new
SimpleClientHttpRequestFactory();
-
-
.....
-
}
而HttpAccessor中使用的是SimpleClientHttpRequestFactory类。
接着走进SimpleClientHttpRequestFactory的createRequest方法
-
public ClientHttpRequest
createRequest
(URI uri, HttpMethod httpMethod)
throws IOException {
-
//打开连接
-
HttpURLConnection
connection
= openConnection(uri.toURL(),
this.proxy);
-
//前期准备工作
-
prepareConnection(connection, httpMethod.name());
-
//默认为true
-
if (
this.bufferRequestBody) {
-
return
new
SimpleBufferingClientHttpRequest(connection,
this.outputStreaming);
-
}
-
else {
-
return
new
SimpleStreamingClientHttpRequest(connection,
this.chunkSize,
this.outputStreaming);
-
}
-
}
openConnection(uri.toURL(), this.proxy)
-
protected HttpURLConnection
openConnection
(URL url, @Nullable Proxy proxy)
throws IOException {
-
URLConnection
urlConnection
= (proxy !=
null ? url.openConnection(proxy) : url.openConnection());
-
if (!(urlConnection
instanceof HttpURLConnection)) {
-
throw
new
IllegalStateException(
-
"HttpURLConnection required for [" + url +
"] but got: " + urlConnection);
-
}
-
return (HttpURLConnection) urlConnection;
-
}
到这里,仿佛明白了什么,原来RestTemplate对原生的http请求URLConnection 进行了一层封装。
prepareConnection(connection, httpMethod.name())
-
protected
void
prepareConnection
(HttpURLConnection connection, String httpMethod)
throws IOException {
-
//设置连接超时时间
-
if (
this.connectTimeout >=
0) {
-
connection.setConnectTimeout(
this.connectTimeout);
-
}
-
//设置读取超时时间
-
if (
this.readTimeout >=
0) {
-
connection.setReadTimeout(
this.readTimeout);
-
}
-
//设置请求方法等
-
boolean
mayWrite
=
-
(
"POST".equals(httpMethod) ||
"PUT".equals(httpMethod) ||
-
"PATCH".equals(httpMethod) ||
"DELETE".equals(httpMethod));
-
-
connection.setDoInput(
true);
-
connection.setInstanceFollowRedirects(
"GET".equals(httpMethod));
-
connection.setDoOutput(mayWrite);
-
connection.setRequestMethod(httpMethod);
-
}
prepareConnection比较简单,进行了一些简单的设置工作。当我们自己利用URLConnection进行http请求时,这些设置也是我们必须要去做的,RestTemplate只是封装好了罢了
createRequest流程走完了,返回了一个封装URLConnection的ClientHttpRequest对象
我把doExecute方法再抄一遍,一方面为了不用再翻上去,一方面也能加深大家的印象。
-
protected <T> T
doExecute
(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
-
@Nullable ResponseExtractor<T> responseExtractor)
throws RestClientException {
-
-
Assert.notNull(url,
"URI is required");
-
Assert.notNull(method,
"HttpMethod is required");
-
ClientHttpResponse
response
=
null;
-
try {
-
//创建文章开头所说的ClientHttpRequest
-
ClientHttpRequest
request
= createRequest(url, method);
-
if (requestCallback !=
null) {
-
//执行请求回调
-
requestCallback.doWithRequest(request);
-
}
-
//执行请求,U获取响应结果
-
response = request.execute();
-
//处理响应结果
-
handleResponse(url, method, response);
-
//利用响应抽取器抽取data返回预先定义的java对象,例如例子中的String
-
return (responseExtractor !=
null ? responseExtractor.extractData(response) :
null);
-
}
-
catch (IOException ex) {
-
String
resource
= url.toString();
-
String
query
= url.getRawQuery();
-
resource = (query !=
null ? resource.substring(
0, resource.indexOf(
'?')) : resource);
-
throw
new
ResourceAccessException(
"I/O error on " + method.name() +
-
" request for \"" + resource +
"\": " + ex.getMessage(), ex);
-
}
-
finally {
-
if (response !=
null) {
-
response.close();
-
}
-
}
-
}
接下来到了requestCallback.doWithRequest(request),即在执行请求之前,先去执行请求回调的doWithRequest方法
-
public
void
doWithRequest
(ClientHttpRequest request)
throws IOException {
-
if (
this.responseType !=
null) {
-
List<MediaType> allSupportedMediaTypes = getMessageConverters().stream()
-
.filter(converter -> canReadResponse(
this.responseType, converter))
-
.flatMap((HttpMessageConverter<?> converter) -> getSupportedMediaTypes(
this.responseType, converter))
-
.distinct()
-
.sorted(MediaType.SPECIFICITY_COMPARATOR)
-
.collect(Collectors.toList());
-
if (logger.isDebugEnabled()) {
-
logger.debug(
"Accept=" + allSupportedMediaTypes);
-
}
-
request.getHeaders().setAccept(allSupportedMediaTypes);
-
}
-
}
这段代码的主要逻辑,是将支持的MediaType设置进请求头中
回到主流程中,接下来需要真正去执行调用了,即request.execute(),
前面了解到,这个request是SimpleBufferingClientHttpRequest类型的,先看一下该类的类图结构

execute方法在AbstractClientHttpRequest类中
-
public
final ClientHttpResponse
execute
()
throws IOException {
-
//检查ClientHttpRequest是否被执行过,如果执行过,则直接报错
-
assertNotExecuted();
-
ClientHttpResponse
result
= executeInternal(
this.headers);
-
//在执行请求之后,将标志位设置为true
-
this.executed =
true;
-
return result;
-
}
进入executeInternal方法中,位于AbstractBufferingClientHttpRequest中
-
protected ClientHttpResponse
executeInternal
(HttpHeaders headers)
throws IOException {
-
byte[] bytes =
this.bufferedOutput.toByteArray();
-
if (headers.getContentLength() <
0) {
-
headers.setContentLength(bytes.length);
-
}
-
ClientHttpResponse
result
= executeInternal(headers, bytes);
-
this.bufferedOutput =
new
ByteArrayOutputStream(
0);
-
return result;
-
}
其中核心的是executeInternal(headers, bytes)方法,位于SimpleBufferingClientHttpRequest中
-
protected ClientHttpResponse
executeInternal
(HttpHeaders headers, byte[] bufferedOutput)
throws IOException {
-
//将请求头设置进connection中
-
addHeaders(
this.connection, headers);
-
// JDK <1.8 doesn't support getOutputStream with HTTP DELETE
-
if (getMethod() == HttpMethod.DELETE && bufferedOutput.length ==
0) {
-
this.connection.setDoOutput(
false);
-
}
-
if (
this.connection.getDoOutput() &&
this.outputStreaming) {
-
this.connection.setFixedLengthStreamingMode(bufferedOutput.length);
-
}
-
this.connection.connect();
-
if (
this.connection.getDoOutput()) {
-
//将缓存区的数据复制进connection的输出流中
-
FileCopyUtils.copy(bufferedOutput,
this.connection.getOutputStream());
-
}
-
else {
-
// Immediately trigger the request in a no-output scenario as well
-
this.connection.getResponseCode();
-
}
-
return
new
SimpleClientHttpResponse(
this.connection);
-
}
该方法使用HttpURLConnection与服务端建立了连接,并返回一个SimpleClientHttpResponse。单纯包裹着此HttpURLConnection对象。
request.execute()也就走完了,接着走主流程的下一步
再贴一遍主流程
-
protected <T> T
doExecute
(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
-
@Nullable ResponseExtractor<T> responseExtractor)
throws RestClientException {
-
-
Assert.notNull(url,
"URI is required");
-
Assert.notNull(method,
"HttpMethod is required");
-
ClientHttpResponse
response
=
null;
-
try {
-
//创建文章开头所说的ClientHttpRequest
-
ClientHttpRequest
request
= createRequest(url, method);
-
if (requestCallback !=
null) {
-
//执行请求回调
-
requestCallback.doWithRequest(request);
-
}
-
//执行请求,获取响应结果
-
response = request.execute();
-
//处理响应结果
-
handleResponse(url, method, response);
-
//利用响应抽取器抽取data返回预先定义的java对象,例如例子中的String
-
return (responseExtractor !=
null ? responseExtractor.extractData(response) :
null);
-
}
-
catch (IOException ex) {
-
String
resource
= url.toString();
-
String
query
= url.getRawQuery();
-
resource = (query !=
null ? resource.substring(
0, resource.indexOf(
'?')) : resource);
-
throw
new
ResourceAccessException(
"I/O error on " + method.name() +
-
" request for \"" + resource +
"\": " + ex.getMessage(), ex);
-
}
-
finally {
-
if (response !=
null) {
-
response.close();
-
}
-
}
-
}
handleResponse(url, method, response)
-
protected
void
handleResponse
(URI url, HttpMethod method, ClientHttpResponse response)
throws IOException {
-
//获取错误处理器
-
ResponseErrorHandler
errorHandler
= getErrorHandler();
-
boolean
hasError
= errorHandler.hasError(response);
-
if (logger.isDebugEnabled()) {
-
try {
-
int
code
= response.getRawStatusCode();
-
HttpStatus
status
= HttpStatus.resolve(code);
-
logger.debug(
"Response " + (status !=
null ? status : code));
-
}
-
catch (IOException ex) {
-
// ignore
-
}
-
}
-
//如果出现错误的话,例如状态码为500,则进入到错误处理流程中
-
if (hasError) {
-
errorHandler.handleError(url, method, response);
-
}
-
}
这一步主要是检查状态码是否是4或者5开头的,如果是的话,则走相应的错误处理流程。
如果没有错误的话,则进入到最后一步
responseExtractor.extractData(response),抽取响应中的数据
responseExtractor是HttpMessageConverterExtractor类型的
-
public T
extractData
(ClientHttpResponse response)
throws IOException {
-
MessageBodyClientHttpResponseWrapper
responseWrapper
=
new
MessageBodyClientHttpResponseWrapper(response);
-
if (!responseWrapper.hasMessageBody() || responseWrapper.hasEmptyMessageBody()) {
-
return
null;
-
}
-
MediaType
contentType
= getContentType(responseWrapper);
-
-
try {
-
//遍历HttpMessageConverter
-
for (HttpMessageConverter<?> messageConverter :
this.messageConverters) {
-
if (messageConverter
instanceof GenericHttpMessageConverter) {
-
GenericHttpMessageConverter<?> genericMessageConverter =
-
(GenericHttpMessageConverter<?>) messageConverter;
-
if (genericMessageConverter.canRead(
this.responseType,
null, contentType)) {
-
if (logger.isDebugEnabled()) {
-
ResolvableType
resolvableType
= ResolvableType.forType(
this.responseType);
-
logger.debug(
"Reading to [" + resolvableType +
"]");
-
}
-
return (T) genericMessageConverter.read(
this.responseType,
null, responseWrapper);
-
}
-
}
-
if (
this.responseClass !=
null) {
-
//如果该messageConverter能够读取该contentType,并且能转化成responseClass类型
-
if (messageConverter.canRead(
this.responseClass, contentType)) {
-
if (logger.isDebugEnabled()) {
-
String
className
=
this.responseClass.getName();
-
logger.debug(
"Reading to [" + className +
"] as \"" + contentType +
"\"");
-
}
-
//将响应数据转化成对应的java类型
-
return (T) messageConverter.read((Class)
this.responseClass, responseWrapper);
-
}
-
}
-
}
-
}
-
catch (IOException | HttpMessageNotReadableException ex) {
-
throw
new
RestClientException(
"Error while extracting response for type [" +
-
this.responseType +
"] and content type [" + contentType +
"]", ex);
-
}
-
-
throw
new
UnknownContentTypeException(
this.responseType, contentType,
-
response.getRawStatusCode(), response.getStatusText(), response.getHeaders(),
-
getResponseBody(response));
-
}
extractData和核心逻辑就是,遍历所有的HttpMessageConverter,如果发现某个messageConverter能够读取响应数据,则返回转化后的数据。
主流程最后一步,则是将封装的HttpURLConnection的输入流关闭。
到这里,RestTemplate的源码分析就结束了。
一句话概括RestTemplate,
RestTemplate封装了原生的HttpURLConnection,采用Restful的理念,更优雅地来完成对HTTP服务的调用。
本文详细分析了SpringBoot中RestTemplate的使用、类图结构、构造方法以及执行HTTP请求的核心逻辑。RestTemplate通过封装HttpURLConnection,简化了HTTP请求操作,提供了如getForObject等便捷方法。在执行请求时,它会根据响应类型选择合适的HttpMessageConverter进行数据转换。通过对源码的剖析,我们可以更深入理解RestTemplate如何优雅地处理HTTP调用。
775

被折叠的 条评论
为什么被折叠?



