Java 在带有泛型参数的泛型方法中使用Spring RestTemplate
要在SpringRESTTemplate中使用泛型类型,我们需要使用Java 在带有泛型参数的泛型方法中使用Spring RestTemplate,java,spring,generics,jackson,resttemplate,Java,Spring,Generics,Jackson,Resttemplate,要在SpringRESTTemplate中使用泛型类型,我们需要使用ParameterizedTypeReference() 假设我有课 public class MyClass { int users[]; public int[] getUsers() { return users; } public void setUsers(int[] users) {this.users = users;} } 还有一些包装类 public class ResponseWr
ParameterizedTypeReference
()
假设我有课
public class MyClass {
int users[];
public int[] getUsers() { return users; }
public void setUsers(int[] users) {this.users = users;}
}
还有一些包装类
public class ResponseWrapper <T> {
T response;
public T getResponse () { return response; }
public void setResponse(T response) {this.response = response;}
}
。。。我得到的不是ResponseEntity
对象,而是ResponseEntity
对象
我怎样才能解决这个问题?这是一个RestTemplate错误吗
更新1
感谢@Sotirios,我理解了这个概念。不幸的是,我是新注册的,所以我不能评论他的答案,所以就写在这里。我不确定我是否清楚如何使用
Map
和Class
键(由@Sotirios在其答案末尾提出)实施拟议的方法来解决我的问题。有人能举个例子吗?不,这不是一个bug。这是参数化typereference
hack如何工作的结果
如果您看一下它的实现,它使用了哪些状态
返回表示实体的直接超类的类型
(类、接口、基元类型或void)由该类表示
如果超类是参数化类型,返回类型
对象
必须准确反映源中使用的实际类型参数
代码。
所以,如果你使用
new ParameterizedTypeReference<ResponseWrapper<MyClass>>() {}
new ParameterizedTypeReference<ResponseWrapper<T>>() {}
它将准确地为ResponseWrapper
返回类型
,因为这就是它在源代码中的显示方式
当Spring看到T
,它实际上是一个TypeVariable
对象时,它不知道要使用的类型,所以它使用它的默认值
您不能使用您建议的方式使用ParameterizedTypeReference
,使其在接受任何类型的意义上具有通用性。考虑编写一个代码< >代码>代码>键<代码>类<代码>映射到预定义的<代码>参数化的TyPeRe><代码>该类。
您可以子类化
ParameterizedTypeReference
并重写其getType
方法,以返回正确创建的ParameterizedType
。不,这不是错误。这是参数化typereference
hack如何工作的结果
如果您看一下它的实现,它使用了哪些状态
返回表示实体的直接超类的类型
(类、接口、基元类型或void)由该类表示
如果超类是参数化类型,返回类型
对象
必须准确反映源中使用的实际类型参数
代码。
所以,如果你使用
new ParameterizedTypeReference<ResponseWrapper<MyClass>>() {}
new ParameterizedTypeReference<ResponseWrapper<T>>() {}
它将准确地为ResponseWrapper
返回类型
,因为这就是它在源代码中的显示方式
当Spring看到T
,它实际上是一个TypeVariable
对象时,它不知道要使用的类型,所以它使用它的默认值
您不能使用您建议的方式使用ParameterizedTypeReference
,使其在接受任何类型的意义上具有通用性。考虑编写一个代码< >代码>代码>键<代码>类<代码>映射到预定义的<代码>参数化的TyPeRe><代码>该类。
您可以子类化
ParameterizedTypeReference
并重写其getType
方法以返回正确创建的ParameterizedType
。正如Sotirios所解释的,您不能使用ParameterizedTypeReference
,但是ParameterizedTypeReference仅用于向对象映射器提供类型
,并且由于您拥有在类型擦除时被删除的类,因此您可以创建自己的类并将其传递给RestTemplate
,以便对象映射器可以重建您需要的对象
首先,您需要实现ParameterizedType接口,您可以在GoogleGSON项目中找到一个实现。
将实现添加到项目中后,可以像下面这样扩展抽象参数化TypeReference
:
class FakeParameteredTypeReference扩展了ParameteredTypeReference{
@凌驾
公共类型getType(){
类型[]responseWrapperActualTypes={MyClass.class};
ParameteredType responseWrapperType=新的ParameteredTypeImpl(
ResponseWrapper.class,
响应包装器实际类型,
无效的
);
返回响应包装器类型;
}
}
然后您可以将其传递给exchange功能:
template.exchange(
uri,
HttpMethod.POST,
null,
new FakeParameterizedTypeReference<ResponseWrapper<T>>());
template.exchange(
乌里,
HttpMethod.POST,
无效的
新的FakeParameteredTypeReference());
由于存在所有类型信息,对象映射器将正确构造
响应包装器
对象,正如Sotirios所解释的,您不能使用参数化类型引用
,但参数化类型引用仅用于向对象映射器提供类型
,当类型擦除发生时,类会被删除,因此可以创建自己的类并将其传递给restemplate
,以便对象映射器可以重建所需的对象
首先,您需要实现ParameterizedType接口,您可以在GoogleGSON项目中找到一个实现。
将实现添加到项目中后,可以像下面这样扩展抽象参数化TypeReference
:
class FakeParameteredTypeReference扩展了ParameteredTypeReference{
@凌驾
公共类型getType(){
类型[]responseWrapperActualTypes={MyClass.class};
ParameteredType responseWrapperType=新的ParameteredTypeImpl(
ResponseWrapper.class,
响应包装器实际类型,
无效的
);
返回响应包装器类型;
}
}
然后您可以将其传递给exchange功能:
template.exchange(
uri,
HttpMethod.POST,
null,
new FakeParameterizedTypeReference<ResponseWrapper<T>>());
template.exchange(
乌里,
HttpMethod.POST,
无效的
新的FakeParameteredTypeReference());
在所有类型信息存在的情况下,对象映射器将正确地构造
template.exchange(
uri,
HttpMethod.POST,
null,
new FakeParameterizedTypeReference<ResponseWrapper<T>>());
List<HttpMessageConverter<?>> oldConverters = new ArrayList<HttpMessageConverter<?>>();
oldConverters.addAll(template.getMessageConverters());
List<HttpMessageConverter<?>> stringConverter = new ArrayList<HttpMessageConverter<?>>();
stringConverter.add(new StringHttpMessageConverter());
template.setMessageConverters(stringConverter);
ResponseEntity<String> response = template.exchange(uri, HttpMethod.GET, null, String.class);
String body = null;
List<T> result = new ArrayList<T>();
ObjectMapper mapper = new ObjectMapper();
if (response.hasBody()) {
body = items.getBody();
try {
result = mapper.readValue(body, mapper.getTypeFactory().constructCollectionType(List.class, clazz));
} catch (Exception e) {
e.printStackTrace();
} finally {
template.setMessageConverters(oldConverters);
}
...
Map<Class<?>, ParameterizedTypeReference> typeReferences = new HashMap<>();
typeReferences.put(MyClass1.class, new ParameterizedTypeReference<ResponseWrapper<MyClass1>>() { });
typeReferences.put(MyClass2.class, new ParameterizedTypeReference<ResponseWrapper<MyClass2>>() { });
...
ParameterizedTypeReference typeRef = typeReferences.get(clazz);
ResponseEntity<ResponseWrapper<T>> response = restTemplate.exchange(
uri,
HttpMethod.GET,
null,
typeRef);
public <T> ResponseWrapper<T> makeRequest(URI uri, final Class<T> clazz) {
ResponseEntity<ResponseWrapper<T>> response = template.exchange(
uri,
HttpMethod.POST,
null,
new ParameterizedTypeReference<ResponseWrapper<T>>() {
public Type getType() {
return new MyParameterizedTypeImpl((ParameterizedType) super.getType(), new Type[] {clazz});
}
});
return response;
}
public class MyParameterizedTypeImpl implements ParameterizedType {
private ParameterizedType delegate;
private Type[] actualTypeArguments;
MyParameterizedTypeImpl(ParameterizedType delegate, Type[] actualTypeArguments) {
this.delegate = delegate;
this.actualTypeArguments = actualTypeArguments;
}
@Override
public Type[] getActualTypeArguments() {
return actualTypeArguments;
}
@Override
public Type getRawType() {
return delegate.getRawType();
}
@Override
public Type getOwnerType() {
return delegate.getOwnerType();
}
}
static <K, V> TypeToken<Map<K, V>> mapToken(TypeToken<K> keyToken, TypeToken<V> valueToken) {
return new TypeToken<Map<K, V>>() {}
.where(new TypeParameter<K>() {}, keyToken)
.where(new TypeParameter<V>() {}, valueToken);
}
import com.google.common.reflect.TypeToken;
import org.springframework.core.ParameterizedTypeReference;
import java.lang.reflect.Type;
public class ParameterizedTypeReferenceBuilder {
public static <T> ParameterizedTypeReference<T> fromTypeToken(TypeToken<T> typeToken) {
return new TypeTokenParameterizedTypeReference<>(typeToken);
}
private static class TypeTokenParameterizedTypeReference<T> extends ParameterizedTypeReference<T> {
private final Type type;
private TypeTokenParameterizedTypeReference(TypeToken<T> typeToken) {
this.type = typeToken.getType();
}
@Override
public Type getType() {
return type;
}
@Override
public boolean equals(Object obj) {
return (this == obj || (obj instanceof ParameterizedTypeReference &&
this.type.equals(((ParameterizedTypeReference<?>) obj).getType())));
}
@Override
public int hashCode() {
return this.type.hashCode();
}
@Override
public String toString() {
return "ParameterizedTypeReference<" + this.type + ">";
}
}
}
public <T> ResponseWrapper<T> makeRequest(URI uri, Class<T> clazz) {
ParameterizedTypeReference<ResponseWrapper<T>> responseTypeRef =
ParameterizedTypeReferenceBuilder.fromTypeToken(
new TypeToken<ResponseWrapper<T>>() {}
.where(new TypeParameter<T>() {}, clazz));
ResponseEntity<ResponseWrapper<T>> response = template.exchange(
uri,
HttpMethod.POST,
null,
responseTypeRef);
return response;
}
ResponseWrapper<MyClass> result = makeRequest(uri, MyClass.class);
public <T> ResponseWrapper<T> makeRequest(URI uri, TypeToken<T> resultTypeToken) {
ParameterizedTypeReference<ResponseWrapper<T>> responseTypeRef =
ParameterizedTypeReferenceBuilder.fromTypeToken(
new TypeToken<ResponseWrapper<T>>() {}
.where(new TypeParameter<T>() {}, resultTypeToken));
ResponseEntity<ResponseWrapper<T>> response = template.exchange(
uri,
HttpMethod.POST,
null,
responseTypeRef);
return response;
}
ResponseWrapper<List<MyClass>> result = makeRequest(uri, new TypeToken<List<MyClass>>() {});
ResolvableType resolvableType = ResolvableType.forClassWithGenerics(ListResultEntity.class, itemClass);
ParameterizedTypeReference<ListResultEntity<T>> typeRef = ParameterizedTypeReference.forType(resolvableType.getType());
public <T> ResponseWrapper<T> makeRequest(URI uri, Class<T> clazz) {
ResponseEntity<ResponseWrapper<T>> response = template.exchange(
uri,
HttpMethod.POST,
null,
ParameterizedTypeReference.forType(ResolvableType.forClassWithGenerics(ResponseWrapper.class, clazz)));
return response;
}
private <REQ, RES> RES queryRemoteService(String url, HttpMethod method, REQ req, Class reqClass) {
RES result = null;
try {
long startMillis = System.currentTimeMillis();
// Set the Content-Type header
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(new MediaType("application","json"));
// Set the request entity
HttpEntity<REQ> requestEntity = new HttpEntity<>(req, requestHeaders);
// Create a new RestTemplate instance
RestTemplate restTemplate = new RestTemplate();
// Add the Jackson and String message converters
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
// Make the HTTP POST request, marshaling the request to JSON, and the response to a String
ResponseEntity<RES> responseEntity = restTemplate.exchange(url, method, requestEntity, reqClass);
result = responseEntity.getBody();
long stopMillis = System.currentTimeMillis() - startMillis;
Log.d(TAG, method + ":" + url + " took " + stopMillis + " ms");
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
return result;
}
public class ValidateRequest {
User user;
User checkedUser;
Vehicle vehicle;
}
public class UserResponse {
User user;
RequestResult requestResult;
}
public User checkUser(User user, String checkedUserName) {
String url = urlBuilder()
.add(USER)
.add(USER_CHECK)
.build();
ValidateRequest request = new ValidateRequest();
request.setUser(user);
request.setCheckedUser(new User(checkedUserName));
UserResponse response = queryRemoteService(url, HttpMethod.POST, request, UserResponse.class);
return response.getUser();
}
private static <T> ParameterizedTypeReference<BaseResponse<T>> typeReferenceOf ( Class<T> tClass ) {
return ParameterizedTypeReference.forType( sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl.make( BaseResponse.class, new Type[]{ tClass }, null ) );
}
@Getter
@Setter
public static class BaseResponse<T> {
private ResponseData<T> response;
public BaseResponse () { }
public boolean hasData () {
return response != null;
}
public T data () {
return response.data;
}
}
@Getter
@Setter
public static final class ResponseData<T> {
private T data;
public ResponseData () { }
}
public <T> Mono <T> get ( URI uri, Class<T> tClass ) {
return webClient
.get ()
.uri ( uriBuilder -> uriBuilder.pathSegment( uri.getPath() ).build() )
.exchangeToMono ( clientResponse -> clientResponse.bodyToMono( typeReferenceOf( tClass ) ) )
.flatMap ( baseResponse -> baseResponse.hasData() ? Mono.just( baseResponse.data() ) : Mono.empty() );
}