Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support generic target types in the RestTemplate
This change makes it possible to use the RestTemplate to read an HTTP response into a target generic type object. The RestTemplate has three new exchange(...) methods that accept ParameterizedTypeReference -- a new class that enables capturing and passing generic type info. See the Javadoc of the three new methods in RestOperations for a short example. To support this feature, the HttpMessageConverter is now extended by GenericHttpMessageConverter, which adds a method for reading an HttpInputMessage to a specific generic type. The new interface is implemented by the MappingJacksonHttpMessageConverter and also by a new Jaxb2CollectionHttpMessageConverter that can read read a generic Collection where the generic type is a JAXB type annotated with @XmlRootElement or @XmlType. Issue: SPR-7023
- Loading branch information
1 parent
789e12a
commit ed3823b
Showing
14 changed files
with
1,213 additions
and
99 deletions.
There are no files selected for viewing
99 changes: 99 additions & 0 deletions
99
spring-core/src/main/java/org/springframework/core/ParameterizedTypeReference.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
/* | ||
* Copyright 2002-2012 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. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.springframework.core; | ||
|
||
import java.lang.reflect.ParameterizedType; | ||
import java.lang.reflect.Type; | ||
|
||
import org.springframework.util.Assert; | ||
|
||
/** | ||
* The purpose of this class is to enable capturing and passing a generic | ||
* {@link Type}. In order to capture the generic type and retain it at runtime, | ||
* you need to create a sub-class as follows: | ||
* | ||
* <pre class="code"> | ||
* ParameterizedTypeReference<List<String>> typeRef = new ParameterizedTypeReference<List<String>>() {}; | ||
* </pre> | ||
* | ||
* <p>The resulting {@code typeReference} instance can then be used to obtain a | ||
* {@link Type} instance that carries parameterized type information. | ||
* For more information on "super type tokens" see the link to Neal Gafter's blog post. | ||
* | ||
* @author Arjen Poutsma | ||
* @author Rossen Stoyanchev | ||
* @since 3.2 | ||
* | ||
* @see http://gafter.blogspot.nl/2006/12/super-type-tokens.html | ||
*/ | ||
public abstract class ParameterizedTypeReference<T> { | ||
|
||
private final Type type; | ||
|
||
protected ParameterizedTypeReference() { | ||
Class<?> parameterizedTypeReferenceSubClass = findParameterizedTypeReferenceSubClass(getClass()); | ||
|
||
Type type = parameterizedTypeReferenceSubClass.getGenericSuperclass(); | ||
Assert.isInstanceOf(ParameterizedType.class, type); | ||
|
||
ParameterizedType parameterizedType = (ParameterizedType) type; | ||
Assert.isTrue(parameterizedType.getActualTypeArguments().length == 1); | ||
|
||
this.type = parameterizedType.getActualTypeArguments()[0]; | ||
} | ||
|
||
private static Class<?> findParameterizedTypeReferenceSubClass(Class<?> child) { | ||
|
||
Class<?> parent = child.getSuperclass(); | ||
|
||
if (Object.class.equals(parent)) { | ||
throw new IllegalStateException("Expected ParameterizedTypeReference superclass"); | ||
} | ||
else if (ParameterizedTypeReference.class.equals(parent)) { | ||
return child; | ||
} | ||
else { | ||
return findParameterizedTypeReferenceSubClass(parent); | ||
} | ||
} | ||
|
||
public Type getType() { | ||
return this.type; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) { | ||
return true; | ||
} | ||
if (o instanceof ParameterizedTypeReference) { | ||
ParameterizedTypeReference<?> other = (ParameterizedTypeReference<?>) o; | ||
return this.type.equals(other.type); | ||
} | ||
return false; | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return this.type.hashCode(); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "ParameterizedTypeReference<" + this.type + ">"; | ||
} | ||
} |
61 changes: 61 additions & 0 deletions
61
spring-core/src/test/java/org/springframework/core/ParameterizedTypeReferenceTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
/* | ||
* Copyright 2002-2012 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. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.springframework.core; | ||
|
||
import java.lang.reflect.Type; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
import static org.junit.Assert.assertEquals; | ||
import org.junit.Test; | ||
|
||
/** | ||
* Test fixture for {@link ParameterizedTypeReference}. | ||
* | ||
* @author Arjen Poutsma | ||
* @author Rossen Stoyanchev | ||
*/ | ||
public class ParameterizedTypeReferenceTest { | ||
|
||
@Test | ||
public void map() throws NoSuchMethodException { | ||
Type mapType = getClass().getMethod("mapMethod").getGenericReturnType(); | ||
ParameterizedTypeReference<Map<Object,String>> mapTypeReference = new ParameterizedTypeReference<Map<Object,String>>() {}; | ||
assertEquals(mapType, mapTypeReference.getType()); | ||
} | ||
|
||
@Test | ||
public void list() throws NoSuchMethodException { | ||
Type mapType = getClass().getMethod("listMethod").getGenericReturnType(); | ||
ParameterizedTypeReference<List<String>> mapTypeReference = new ParameterizedTypeReference<List<String>>() {}; | ||
assertEquals(mapType, mapTypeReference.getType()); | ||
} | ||
|
||
@Test | ||
public void string() { | ||
ParameterizedTypeReference<String> typeReference = new ParameterizedTypeReference<String>() {}; | ||
assertEquals(String.class, typeReference.getType()); | ||
} | ||
|
||
public static Map<Object, String> mapMethod() { | ||
return null; | ||
} | ||
|
||
public static List<String> listMethod() { | ||
return null; | ||
} | ||
} |
60 changes: 60 additions & 0 deletions
60
spring-web/src/main/java/org/springframework/http/converter/GenericHttpMessageConverter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
/* | ||
* Copyright 2002-2012 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. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.springframework.http.converter; | ||
|
||
import java.io.IOException; | ||
import java.lang.reflect.Type; | ||
|
||
import org.springframework.core.ParameterizedTypeReference; | ||
import org.springframework.http.HttpInputMessage; | ||
import org.springframework.http.MediaType; | ||
|
||
/** | ||
* A specialization of {@link HttpMessageConverter} that can convert an HTTP | ||
* request into a target object of a specified generic type. | ||
* | ||
* @author Arjen Poutsma | ||
* @since 3.2 | ||
* | ||
* @see ParameterizedTypeReference | ||
*/ | ||
public interface GenericHttpMessageConverter<T> extends HttpMessageConverter<T> { | ||
|
||
/** | ||
* Indicates whether the given type can be read by this converter. | ||
* @param type the type to test for readability | ||
* @param mediaType the media type to read, can be {@code null} if not specified. | ||
* Typically the value of a {@code Content-Type} header. | ||
* @return {@code true} if readable; {@code false} otherwise | ||
*/ | ||
boolean canRead(Type type, MediaType mediaType); | ||
|
||
/** | ||
* Read an object of the given type form the given input message, and returns it. | ||
* @param clazz the type of object to return. This type must have previously | ||
* been passed to the {@link #canRead canRead} method of this interface, | ||
* which must have returned {@code true}. | ||
* @param type the type of the target object | ||
* @param inputMessage the HTTP input message to read from | ||
* @return the converted object | ||
* @throws IOException in case of I/O errors | ||
* @throws HttpMessageNotReadableException in case of conversion errors | ||
*/ | ||
T read(Type type, HttpInputMessage inputMessage) | ||
throws IOException, HttpMessageNotReadableException; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.