Java 列表返回类型上具有Rest模板的泛型方法?
我正在尝试创建一个通用的rest模板方法。目标是反序列化为泛型类型,即使该类型已参数化。不幸的是,当我尝试反序列化时,会得到一个哈希映射列表或类似的东西。字体丢失了。这是我们的代码:Java 列表返回类型上具有Rest模板的泛型方法?,java,spring,spring-boot,Java,Spring,Spring Boot,我正在尝试创建一个通用的rest模板方法。目标是反序列化为泛型类型,即使该类型已参数化。不幸的是,当我尝试反序列化时,会得到一个哈希映射列表或类似的东西。字体丢失了。这是我们的代码: public List<UserValueDTO> getUserDetails() throws Exception { return getResponse("/user/details"); } private <T> T getResponse(String endpoin
public List<UserValueDTO> getUserDetails() throws Exception {
return getResponse("/user/details");
}
private <T> T getResponse(String endpoint) throws Exception {
String token = authenticationService.getToken();
LinkedMultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
headers.add("Authorization", "Bearer " + token);
HttpEntity<?> httpEntity = new HttpEntity<>(null, headers);
try {
ResponseEntity<T> response = restTemplate.exchange(baseUrl + endpoint,
HttpMethod.GET, httpEntity, new ParameterizedTypeReference<T> {});
return response.getBody();
} catch (HttpClientErrorException ex) {
throw new Exception("Unable to get a response from service");
}
}
public List getUserDetails()引发异常{
返回getResponse(“/user/details”);
}
私有T getResponse(字符串端点)引发异常{
String token=authenticationService.getToken();
LinkedMultiValueMap头=新建LinkedMultiValueMap();
添加(“授权”、“承载人”+令牌);
HttpEntity HttpEntity=新的HttpEntity(空,标题);
试一试{
ResponseEntity response=restemplate.exchange(baseUrl+endpoint,
HttpMethod.GET,httpEntity,新的参数化类型引用{});
返回response.getBody();
}捕获(HttpClientErrorException-ex){
抛出新异常(“无法从服务获取响应”);
}
}
我们还编写了一个自动测试来揭示问题。这是测试
@Test
public void requestUserDetails_getsUserDetailsFromExternalService() throws Exception {
doReturn("abcdefg-123456").when(mockAuthenticationService).getToken();
server.expect(requestTo("http://localhost:8090/user/details"))
.andExpect(method(HttpMethod.GET))
.andExpect(header("Authorization", "Bearer abcdefg-123456"))
.andRespond(withSuccess("[\n" +
" {\n" +
" \"userId\": 12345,\n" +
" \"value\": 500.0\n" +
" },\n" +
" {\n" +
" \"userId\": 5555,\n" +
" \"value\": 300.0\n" +
" }\n" +
"]", MediaType.APPLICATION_JSON));
List<UserValueDTO> userValueDTOs = accountServiceClient.getUserDetails();
assertThat(userValueDTOs).containsExactlyInAnyOrder(
new UserValueDTO.Builder().userId(12345).value(500.0).build(),
new UserValueDTO.Builder().userId(5555).value(300.0).build()
);
}
@测试
public void requestUserDetails\u GetSuserDetails RomexternalService()引发异常{
doReturn(“abcdefg-123456”).when(mockAuthenticationService).getToken();
expect(requestTo)(“http://localhost:8090/user/details"))
.andExpect(方法(HttpMethod.GET))
.andExpect(标题(“授权”、“持有人abcdefg-123456”))
.andRespond(成功(“[\n”)+
“{\n”+
“用户ID”:12345\n+
“\“值\”:500.0\n”+
},\n+
“{\n”+
“用户ID”:5555\n+
“\“值\”:300.0\n”+
“}\n”+
“]”,MediaType.APPLICATION_JSON));
List userValueDTOs=accountServiceClient.getUserDetails();
assertThat(userValueDTOs).在Yorder中实际包含(
新的UserValueDTO.Builder().userId(12345).value(500.0).build(),
新建UserValueDTO.Builder().userId(5555).value(300.0).build()
);
}
针对此代码运行此测试的结果为:
java.lang.AssertionError:
Expecting:
<[{"userId"=12345, "value"=500.0}, {"userId"=5555, "value"=300.0}]>
to contain exactly in any order:
<[com.project.dto.UserValueDTO@408516e7,
com.project.dto.UserValueDTO@407560ad]>
elements not found:
<[com.project.dto.UserValueDTO@408516e7,
com.project.dto.UserValueDTO@407560ad]>
and elements not expected:
<[{"userId"=12345, "value"=500.0}, {"userId"=5555, "value"=300.0}]>
java.lang.AssertionError:
期望:
以任何顺序完全包含:
未找到元素:
以及不预期的因素:
反序列化时,有没有办法不丢失列表上的参数化类型?谢谢 根据我们在评论中的交流,问题是如何使用
新的ParameterizedTypeReference(){}
。这不起作用的原因与这些类型标记/类型引用/等对象的工作方式有关。(Neal Gafter的博客文章最初对此进行了描述。)
抽象类TypeRefExample{
最终类型typeOfT;
TypeRefExample(){
//获取扩展子类。
//(通常是一个匿名类,如TypeRefExample(){}。)
Class subclass=getClass();
//获取泛型超类,它是
//在扩展条款中。
//这可以是明确的,如:
//类FooTypeRef扩展了TypeRefExample{}
//或者在匿名类的情况下是隐式的:
//新的TypeRefExample(){}
//它声明了一个类,类似于:
//类OuterClass$1扩展了TypeRefExample{}
类型superclass=subclass.getGenericSuperclass();
//如果超类是参数化类型
//可以检索其类型参数。
typeOfT=((参数化类型)超类)
.getActualTypeArguments()[0];
//真正的实现应该做更多的工作
//工作,但这是最基本的想法。
}
}
因此,如果您有一个新的ParameterizedTypeReference(){}
,则getClass().getGenericSuperclass()
返回隐式extends子句中的类型,即ParameterizedTypeReference
,其类型参数是类型变量T
如果使用此参数化TypeReference
对于使测试正常工作至关重要,那么很遗憾,您必须将其作为参数传入。必须使用实际类型参数新建ParameterizedTypeReference(){}
创建它
Guava对解析类上声明的类型变量有一些建议,但对方法上声明的类型变量也有一些建议,实际上是不可能的,因为在调用站点无法将类型参数具体化为方法。我帮不上忙,因为我不知道Spring,但是
newparametedtypereference{}
在我看来非常可疑,因为我知道这样的类是如何工作的。这不会创建参数化类型引用
。它将创建一个ParameterizedTypeReference
,其类型参数是java.lang.reflect.TypeVariable
的实例,该实例由方法getResponse
声明。尝试System.out.println(新的参数化类型引用{}.getType())代码>明白我的意思。@Radiodef-Yikes。上面说类型是“T”。那不好。这里有没有其他方法来获取泛型的参数化类型?不幸的是,您必须将其作为参数传递给方法。你必须用你想要的实际类型参数来实例化ParameterizedTypeReference
,比如new ParameterizedTypeReference(){}
@Radiodef这不是世界上最漂亮的东西,但它正是我们要找的!谢谢有一个问题,您的泛型方法是否应该具有类似私有列表getResponse(字符串端点)的签名,并因此使用List而不是ResponseEntity和ParameteredTypeReference?
abstract class TypeRefExample<T> {
final Type typeOfT;
TypeRefExample() {
// Get the extending subclass.
// (Usually an anonymous class like TypeRefExample<String>() {}.)
Class<?> subclass = getClass();
// Get the generic superclass, which is the type
// in the extends clause.
// This can be explicit like:
// class FooTypeRef extends TypeRefExample<Foo> {}
// or implicit as in the case of an anonymous class:
// new TypeRefExample<Foo>() {}
// which declares a class something like:
// class OuterClass$1 extends TypeRefExample<Foo> {}
Type superclass = subclass.getGenericSuperclass();
// Then if the superclass is a ParameterizedType
// its type argument can be retrieved.
typeOfT = ((ParameterizedType) superclass)
.getActualTypeArguments()[0];
// A real implementation should do some more
// work but that's the basic idea.
}
}