1.1 前言

  • Spring RestTemplateSpring 提供的用于访问 Rest 服务的客户端,此类位于org.springframework.web包下,有了这个工具类,访问第三方服务接口的时候太香了,都不用关心请求参数如何封装,响应参数如何解析,RestTemplate就能帮我们完成这些事项

2.1 使用

  • Spring RestTemplateRest 服务封装了各种方法,使用者可以按照需要使用合适的方法

3.1 解析

类解析

  • 类继承关系图

HttpAccessor 类

  • 这个抽象类是RestTemplate的最顶级接口,下面可以看到主要有两个成员变量 requestFactoryclientHttpRequestInitializers
    • ClientHttpRequestFactory接口只有方法ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException;,此方法是用来创建请求
    • ClientHttpRequestInitializer此成员见名是个请求初始化类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public abstract class HttpAccessor {

/** Logger available to subclasses. */
protected final Log logger = HttpLogging.forLogName(getClass());

private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();

private final List<ClientHttpRequestInitializer> clientHttpRequestInitializers = new ArrayList<>();

public void setRequestFactory(ClientHttpRequestFactory requestFactory) {
Assert.notNull(requestFactory, "ClientHttpRequestFactory must not be null");
this.requestFactory = requestFactory;
}

public ClientHttpRequestFactory getRequestFactory() {
return this.requestFactory;
}

public void setClientHttpRequestInitializers(
List<ClientHttpRequestInitializer> clientHttpRequestInitializers) {

if (this.clientHttpRequestInitializers != clientHttpRequestInitializers) {
this.clientHttpRequestInitializers.clear();
this.clientHttpRequestInitializers.addAll(clientHttpRequestInitializers);
AnnotationAwareOrderComparator.sort(this.clientHttpRequestInitializers);
}
}

public List<ClientHttpRequestInitializer> getClientHttpRequestInitializers() {
return this.clientHttpRequestInitializers;
}

protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
ClientHttpRequest request = getRequestFactory().createRequest(url, method);
initialize(request);
if (logger.isDebugEnabled()) {
logger.debug("HTTP " + method.name() + " " + url);
}
return request;
}

private void initialize(ClientHttpRequest request) {
this.clientHttpRequestInitializers.forEach(initializer -> initializer.initialize(request));
}

}

InterceptingHttpAccessor 类

  • 此类主要有两个功能
    • 封装拦截器功能,这样的话可以在请求过程中额外做一些事情,比如在SpringCloud环境下使用@LoadBalanced注解修饰RestTemplate的话就可以实现负载均衡的功能,这里面就是使用了拦截器来实现的
    • 创建ClientHttpRequestFactory
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
public abstract class InterceptingHttpAccessor extends HttpAccessor {

private final List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();

@Nullable
private volatile ClientHttpRequestFactory interceptingRequestFactory;


/**
* Set the request interceptors that this accessor should use.
* <p>The interceptors will get immediately sorted according to their
* {@linkplain AnnotationAwareOrderComparator#sort(List) order}.
* @see #getRequestFactory()
* @see AnnotationAwareOrderComparator
*/
public void setInterceptors(List<ClientHttpRequestInterceptor> interceptors) {
// Take getInterceptors() List as-is when passed in here
if (this.interceptors != interceptors) {
this.interceptors.clear();
this.interceptors.addAll(interceptors);
AnnotationAwareOrderComparator.sort(this.interceptors);
}
}

/**
* Get the request interceptors that this accessor uses.
* <p>The returned {@link List} is active and may be modified. Note,
* however, that the interceptors will not be resorted according to their
* {@linkplain AnnotationAwareOrderComparator#sort(List) order} before the
* {@link ClientHttpRequestFactory} is built.
*/
public List<ClientHttpRequestInterceptor> getInterceptors() {
return this.interceptors;
}

@Override
public void setRequestFactory(ClientHttpRequestFactory requestFactory) {
super.setRequestFactory(requestFactory);
this.interceptingRequestFactory = null;
}

@Override
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();
}
}

}

RestTemplate 类

  • 下面可以看到熟悉的List<HttpMessageConverter<?>> messageConverters变量,这里就知道了请求参数如何封装,响应参数如何解析,和Spring Mvc解析参数的原理是一样的

  • static代码主要逻辑就是检查这些类是否存在(是否导入了相关包),然后给这些boolean变量赋值,这样的话在构造函数就可以封装对应的HttpMessageConverter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
public class RestTemplate extends InterceptingHttpAccessor implements RestOperations {

private static final boolean romePresent;

private static final boolean jaxb2Present;

private static final boolean jackson2Present;

private static final boolean jackson2XmlPresent;

private static final boolean jackson2SmilePresent;

private static final boolean jackson2CborPresent;

private static final boolean gsonPresent;

private static final boolean jsonbPresent;

static {
ClassLoader classLoader = RestTemplate.class.getClassLoader();
romePresent = ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", classLoader);
jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", classLoader);
jackson2Present =
ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader);
jackson2CborPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory", classLoader);
gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);
jsonbPresent = ClassUtils.isPresent("javax.json.bind.Jsonb", classLoader);
}


private final List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();

private ResponseErrorHandler errorHandler = new DefaultResponseErrorHandler();

private UriTemplateHandler uriTemplateHandler;

private final ResponseExtractor<HttpHeaders> headersExtractor = new HeadersExtractor();


/**
* Create a new instance of the {@link RestTemplate} using default settings.
* Default {@link HttpMessageConverter HttpMessageConverters} are initialized.
*/
public RestTemplate() {
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(new StringHttpMessageConverter());
this.messageConverters.add(new ResourceHttpMessageConverter(false));
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 (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());
}

if (jackson2SmilePresent) {
this.messageConverters.add(new MappingJackson2SmileHttpMessageConverter());
}
if (jackson2CborPresent) {
this.messageConverters.add(new MappingJackson2CborHttpMessageConverter());
}

this.uriTemplateHandler = initUriTemplateHandler();
}

/**
* Create a new instance of the {@link RestTemplate} based on the given {@link ClientHttpRequestFactory}.
* @param requestFactory the HTTP request factory to use
* @see org.springframework.http.client.SimpleClientHttpRequestFactory
* @see org.springframework.http.client.HttpComponentsClientHttpRequestFactory
*/
public RestTemplate(ClientHttpRequestFactory requestFactory) {
this();
setRequestFactory(requestFactory);
}

/**
* Create a new instance of the {@link RestTemplate} using the given list of
* {@link HttpMessageConverter} to use.
* @param messageConverters the list of {@link HttpMessageConverter} to use
* @since 3.2.7
*/
public RestTemplate(List<HttpMessageConverter<?>> messageConverters) {
validateConverters(messageConverters);
this.messageConverters.addAll(messageConverters);
this.uriTemplateHandler = initUriTemplateHandler();
}

...

具体执行

  • 先来个例子,这里执行一个GET请求,返回一个字符对象
1
2
3
4
5
6
7
8
9
10
11
public class Tests {

@Autowired
private RestTemplate restTemplate;

@Test
public void test1 () {
log.info("输出:{}", restTemplate.getForObject("http://cloud-alibaba-user/user/uriVariables/{id}/{age}", String.class, "songsy", 100));
}

}
  • 然后进入getForObject方法,可以看到只是做了一下参数准备
1
2
3
4
5
6
7
8
@Override
@Nullable
public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
HttpMessageConverterExtractor<T> responseExtractor =
new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
}
  • 进入execute()方法,可以看到这里创建了一个URI expanded,可以看到已经将链接上{name}、{age}参数给替换为实际参数了
1
2
3
4
5
6
public <T> T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback,
@Nullable ResponseExtractor<T> responseExtractor, Object... uriVariables) throws RestClientException {

URI expanded = getUriTemplateHandler().expand(url, uriVariables);
return doExecute(expanded, method, requestCallback, responseExtractor);
}
  • 跟进代码,可以知道下面这个方法会将{name}、{age}参数给替换调,观察PathComponent pathTo对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// org.springframework.web.util.HierarchicalUriComponents#expandInternal
protected HierarchicalUriComponents expandInternal(UriTemplateVariables uriVariables) {
Assert.state(!this.encodeState.equals(EncodeState.FULLY_ENCODED),
"URI components already encoded, and could not possibly contain '{' or '}'.");

// Array-based vars rely on the order below...
String schemeTo = expandUriComponent(getScheme(), uriVariables, this.variableEncoder);
String userInfoTo = expandUriComponent(this.userInfo, uriVariables, this.variableEncoder);
String hostTo = expandUriComponent(this.host, uriVariables, this.variableEncoder);
String portTo = expandUriComponent(this.port, uriVariables, this.variableEncoder);
PathComponent pathTo = this.path.expand(uriVariables, this.variableEncoder);
MultiValueMap<String, String> queryParamsTo = expandQueryParams(uriVariables);
String fragmentTo = expandUriComponent(getFragment(), uriVariables, this.variableEncoder);

return new HierarchicalUriComponents(schemeTo, fragmentTo, userInfoTo,
hostTo, portTo, pathTo, queryParamsTo, this.encodeState, this.variableEncoder);
}
  • {name}、{age}替换参数方法,这里有个正则表达式NAMES_PATTERN匹配\{([^/]+?)}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// org.springframework.web.util.UriComponents#expandUriComponent(java.lang.String, org.springframework.web.util.UriComponents.UriTemplateVariables, java.util.function.UnaryOperator<java.lang.String>)
@Nullable
static String expandUriComponent(@Nullable String source, UriTemplateVariables uriVariables,
@Nullable UnaryOperator<String> encoder) {

if (source == null) {
return null;
}
if (source.indexOf('{') == -1) {
return source;
}
if (source.indexOf(':') != -1) {
source = sanitizeSource(source);
}
Matcher matcher = NAMES_PATTERN.matcher(source);
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
String match = matcher.group(1);
String varName = getVariableName(match);
Object varValue = uriVariables.getValue(varName);
if (UriTemplateVariables.SKIP_VALUE.equals(varValue)) {
continue;
}
String formatted = getVariableValueAsString(varValue);
formatted = encoder != null ? encoder.apply(formatted) : Matcher.quoteReplacement(formatted);
matcher.appendReplacement(sb, formatted);
}
matcher.appendTail(sb);
return sb.toString();
}
  • 得到请求链接之后就是执行请求了,可以看到下面的逻辑很清晰
    • 1、创建了ClientHttpRequest对象来执行请求
    • 2、request.execute();执行
    • 3、得到ClientHttpResponse对象来响应结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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 request = createRequest(url, method);
if (requestCallback != null) {
requestCallback.doWithRequest(request);
}
response = request.execute();
handleResponse(url, method, response);
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();
}
}
}
  • 继续跟进request.execute()方法后执行到下面这个方法
    • this.iterator.hasNext()这里的判断就是执行拦截器方法
    • 如果没有拦截器的话,这里ClientHttpRequest delegate 这里创建的是SimpleBufferingClientHttpRequest对象,此对象就构造了HttpURLConnection对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// org.springframework.http.client.InterceptingClientHttpRequest.InterceptingRequestExecution#execute
private class InterceptingRequestExecution implements ClientHttpRequestExecution {

private final Iterator<ClientHttpRequestInterceptor> iterator;

public InterceptingRequestExecution() {
this.iterator = interceptors.iterator();
}

@Override
public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
if (this.iterator.hasNext()) {
ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();
return nextInterceptor.intercept(request, body, this);
}
else {
HttpMethod method = request.getMethod();
Assert.state(method != null, "No standard HTTP method");
ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), method);
request.getHeaders().forEach((key, value) -> delegate.getHeaders().addAll(key, value));
if (body.length > 0) {
if (delegate instanceof StreamingHttpOutputMessage) {
StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) delegate;
streamingOutputMessage.setBody(outputStream -> StreamUtils.copy(body, outputStream));
}
else {
StreamUtils.copy(body, delegate.getBody());
}
}
return delegate.execute();
}
}
}
  • 继续更新delegate.execute()方法,进入到SimpleBufferingClientHttpRequest#executeInternal方法,这里就看到了执行HttpURLConnection请求的操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// org.springframework.http.client.SimpleBufferingClientHttpRequest#executeInternal

@Override
protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
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);
}
// 执行HttpURLConnection请求
this.connection.connect();
if (this.connection.getDoOutput()) {
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);
}
  • 这里执行完成之后直接将结果封装为SimpleClientHttpResponse对象

  • 得到结果之后就是解析响应结果为具体对象了,下面可以看到就是循环this.messageConverters,那个能解析就解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// org.springframework.web.client.HttpMessageConverterExtractor#extractData
public T extractData(ClientHttpResponse response) throws IOException {
MessageBodyClientHttpResponseWrapper responseWrapper = new MessageBodyClientHttpResponseWrapper(response);
if (!responseWrapper.hasMessageBody() || responseWrapper.hasEmptyMessageBody()) {
return null;
}
MediaType contentType = getContentType(responseWrapper);

try {
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) {
if (messageConverter.canRead(this.responseClass, contentType)) {
if (logger.isDebugEnabled()) {
String className = this.responseClass.getName();
logger.debug("Reading to [" + className + "] as \"" + contentType + "\"");
}
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 RestClientException("Could not extract response: no suitable HttpMessageConverter found " +
"for response type [" + this.responseType + "] and content type [" + contentType + "]");
}
  • 我们这里的返回结果是String对象,所以是这个StringHttpMessageConverter解析类来处理返回结果