Browse Source

Add RestTemplate constructor with custom converters

Prior to this commit, RestTemplate's constructors were all initializing
default HTTPMessageConverters. Its API provides a way to replace
those converters with custom ones, but default converters are already
defined and initialized at that point, which can be an issue in some
cases (performance, classpath...).

This commits adds a new constructor for RestTemplate with a list
of message converters as argument. With this new constructor,
default message converters are never initialized.

Issue: SPR-11351
(cherry picked from commit 425e5a0)
pull/465/head
Brian Clozel 12 years ago committed by Juergen Hoeller
parent
commit
1cb9b9cf52
  1. 73
      spring-web/src/main/java/org/springframework/web/client/RestTemplate.java
  2. 6
      spring-web/src/test/java/org/springframework/web/client/RestTemplateTests.java

73
spring-web/src/main/java/org/springframework/web/client/RestTemplate.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -23,6 +23,7 @@ import java.util.ArrayList; @@ -23,6 +23,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.transform.Source;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
@ -79,14 +80,14 @@ import org.springframework.web.util.UriTemplate; @@ -79,14 +80,14 @@ import org.springframework.web.util.UriTemplate;
* {@link #getForObject(String, Class, Map)}), and are capable of substituting any {@linkplain UriTemplate URI templates}
* in that URL using either a {@code String} variable arguments array, or a {@code Map<String, String>}.
* The string varargs variant expands the given template variables in order, so that
* <pre>
* <pre class="code">
* String result = restTemplate.getForObject("http://example.com/hotels/{hotel}/bookings/{booking}", String.class, "42",
* "21");
* </pre>
* will perform a GET on {@code http://example.com/hotels/42/bookings/21}. The map variant expands the template based
* on variable name, and is therefore more useful when using many variables, or when a single variable is used multiple
* times. For example:
* <pre>
* <pre class="code">
* Map&lt;String, String&gt; vars = Collections.singletonMap("hotel", "42");
* String result = restTemplate.getForObject("http://example.com/hotels/{hotel}/rooms/{hotel}", String.class, vars);
* </pre>
@ -95,7 +96,7 @@ import org.springframework.web.util.UriTemplate; @@ -95,7 +96,7 @@ import org.springframework.web.util.UriTemplate;
* expanded URI multiple times.
*
* <p>Furthermore, the {@code String}-argument methods assume that the URL String is unencoded. This means that
* <pre>
* <pre class="code">
* restTemplate.getForObject("http://example.com/hotel list");
* </pre>
* will perform a GET on {@code http://example.com/hotel%20list}. As a result, any URL passed that is already encoded
@ -114,6 +115,7 @@ import org.springframework.web.util.UriTemplate; @@ -114,6 +115,7 @@ import org.springframework.web.util.UriTemplate;
* requestFactory} and {@link #setErrorHandler(ResponseErrorHandler) errorHandler} bean properties.
*
* @author Arjen Poutsma
* @author Brian Clozel
* @since 3.0
* @see HttpMessageConverter
* @see RequestCallback
@ -137,21 +139,22 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat @@ -137,21 +139,22 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
ClassUtils.isPresent("org.codehaus.jackson.JsonGenerator", RestTemplate.class.getClassLoader());
private final ResponseExtractor<HttpHeaders> headersExtractor = new HeadersExtractor();
private List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
private final List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
private ResponseErrorHandler errorHandler = new DefaultResponseErrorHandler();
private final ResponseExtractor<HttpHeaders> headersExtractor = new HeadersExtractor();
/**
* Create a new instance of the {@link RestTemplate} using default settings.
* Default {@link HttpMessageConverter}s are initialized.
*/
public RestTemplate() {
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(new StringHttpMessageConverter());
this.messageConverters.add(new ResourceHttpMessageConverter());
this.messageConverters.add(new SourceHttpMessageConverter());
this.messageConverters.add(new SourceHttpMessageConverter<Source>());
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
if (romePresent) {
this.messageConverters.add(new AtomFeedHttpMessageConverter());
@ -179,6 +182,17 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat @@ -179,6 +182,17 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
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) {
Assert.notEmpty(messageConverters, "'messageConverters' must not be empty");
this.messageConverters.addAll(messageConverters);
}
/**
* Set the message body converters to use.
@ -186,7 +200,8 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat @@ -186,7 +200,8 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
*/
public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
Assert.notEmpty(messageConverters, "'messageConverters' must not be empty");
this.messageConverters = messageConverters;
this.messageConverters.clear();
this.messageConverters.addAll(messageConverters);
}
/**
@ -238,6 +253,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat @@ -238,6 +253,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... urlVariables)
throws RestClientException {
AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(responseType);
ResponseEntityResponseExtractor<T> responseExtractor =
new ResponseEntityResponseExtractor<T>(responseType);
@ -246,6 +262,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat @@ -246,6 +262,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> urlVariables)
throws RestClientException {
AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(responseType);
ResponseEntityResponseExtractor<T> responseExtractor =
new ResponseEntityResponseExtractor<T>(responseType);
@ -283,6 +300,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat @@ -283,6 +300,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
public URI postForLocation(String url, Object request, Map<String, ?> urlVariables)
throws RestClientException {
HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(request);
HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, this.headersExtractor, urlVariables);
return headers.getLocation();
@ -296,6 +314,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat @@ -296,6 +314,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
public <T> T postForObject(String url, Object request, Class<T> responseType, Object... uriVariables)
throws RestClientException {
HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(request, responseType);
HttpMessageConverterExtractor<T> responseExtractor =
new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger);
@ -304,6 +323,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat @@ -304,6 +323,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
public <T> T postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)
throws RestClientException {
HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(request, responseType);
HttpMessageConverterExtractor<T> responseExtractor =
new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger);
@ -466,8 +486,9 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat @@ -466,8 +486,9 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
}
/**
* Execute the given method on the provided URI. The {@link ClientHttpRequest} is processed using the {@link
* RequestCallback}; the response with the {@link ResponseExtractor}.
* Execute the given method on the provided URI.
* <p>The {@link ClientHttpRequest} is processed using the {@link RequestCallback};
* the response with the {@link ResponseExtractor}.
* @param url the fully-expanded URL to connect to
* @param method the HTTP method to execute (GET, POST, etc.)
* @param requestCallback object that prepares the request (can be {@code null})
@ -501,7 +522,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat @@ -501,7 +522,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
}
catch (IOException ex) {
throw new ResourceAccessException("I/O error on " + method.name() +
" request for \"" + url + "\":" + ex.getMessage(), ex);
" request for \"" + url + "\": " + ex.getMessage(), ex);
}
finally {
if (response != null) {
@ -513,9 +534,8 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat @@ -513,9 +534,8 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
private void logResponseStatus(HttpMethod method, URI url, ClientHttpResponse response) {
if (logger.isDebugEnabled()) {
try {
logger.debug(
method.name() + " request for \"" + url + "\" resulted in " + response.getStatusCode() + " (" +
response.getStatusText() + ")");
logger.debug(method.name() + " request for \"" + url + "\" resulted in " +
response.getStatusCode() + " (" + response.getStatusText() + ")");
}
catch (IOException e) {
// ignore
@ -526,9 +546,8 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat @@ -526,9 +546,8 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
private void handleResponseError(HttpMethod method, URI url, ClientHttpResponse response) throws IOException {
if (logger.isWarnEnabled()) {
try {
logger.warn(
method.name() + " request for \"" + url + "\" resulted in " + response.getStatusCode() + " (" +
response.getStatusText() + "); invoking error handler");
logger.warn(method.name() + " request for \"" + url + "\" resulted in " +
response.getStatusCode() + " (" + response.getStatusText() + "); invoking error handler");
}
catch (IOException e) {
// ignore
@ -554,7 +573,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat @@ -554,7 +573,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
if (responseType != null) {
Class<?> responseClass = null;
if (responseType instanceof Class) {
responseClass = (Class) responseType;
responseClass = (Class<?>) responseType;
}
List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();
@ -566,7 +585,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat @@ -566,7 +585,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
}
else if (converter instanceof GenericHttpMessageConverter) {
GenericHttpMessageConverter genericConverter = (GenericHttpMessageConverter) converter;
GenericHttpMessageConverter<?> genericConverter = (GenericHttpMessageConverter<?>) converter;
if (genericConverter.canRead(responseType, null, null)) {
allSupportedMediaTypes.addAll(getSupportedMediaTypes(converter));
}
@ -604,7 +623,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat @@ -604,7 +623,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
*/
private class HttpEntityRequestCallback extends AcceptHeaderRequestCallback {
private final HttpEntity requestEntity;
private final HttpEntity<?> requestEntity;
private HttpEntityRequestCallback(Object requestBody) {
this(requestBody, null);
@ -614,10 +633,10 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat @@ -614,10 +633,10 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
private HttpEntityRequestCallback(Object requestBody, Type responseType) {
super(responseType);
if (requestBody instanceof HttpEntity) {
this.requestEntity = (HttpEntity) requestBody;
this.requestEntity = (HttpEntity<?>) requestBody;
}
else if (requestBody != null) {
this.requestEntity = new HttpEntity(requestBody);
this.requestEntity = new HttpEntity<Object>(requestBody);
}
else {
this.requestEntity = HttpEntity.EMPTY;
@ -643,7 +662,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat @@ -643,7 +662,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
Class<?> requestType = requestBody.getClass();
HttpHeaders requestHeaders = requestEntity.getHeaders();
MediaType requestContentType = requestHeaders.getContentType();
for (HttpMessageConverter messageConverter : getMessageConverters()) {
for (HttpMessageConverter<?> messageConverter : getMessageConverters()) {
if (messageConverter.canWrite(requestType, requestContentType)) {
if (!requestHeaders.isEmpty()) {
httpRequest.getHeaders().putAll(requestHeaders);
@ -658,7 +677,8 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat @@ -658,7 +677,8 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
}
}
messageConverter.write(requestBody, requestContentType, httpRequest);
((HttpMessageConverter<Object>) messageConverter).write(
requestBody, requestContentType, httpRequest);
return;
}
}
@ -683,7 +703,8 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat @@ -683,7 +703,8 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
public ResponseEntityResponseExtractor(Type responseType) {
if (responseType != null && !Void.class.equals(responseType)) {
this.delegate = new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger);
} else {
}
else {
this.delegate = null;
}
}

6
spring-web/src/test/java/org/springframework/web/client/RestTemplateTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -68,9 +68,9 @@ public class RestTemplateTests { @@ -68,9 +68,9 @@ public class RestTemplateTests {
response = mock(ClientHttpResponse.class);
errorHandler = mock(ResponseErrorHandler.class);
converter = mock(HttpMessageConverter.class);
template = new RestTemplate(requestFactory);
template = new RestTemplate(Collections.<HttpMessageConverter<?>>singletonList(converter));
template.setRequestFactory(requestFactory);
template.setErrorHandler(errorHandler);
template.setMessageConverters(Collections.<HttpMessageConverter<?>>singletonList(converter));
}
@Test

Loading…
Cancel
Save