我试图创建一个通用的休息模板方法。目标是反序列化为泛型类型,即使类型已参数化。不幸的是,当我尝试反序列化时,我得到一个哈希映射列表或类似的东西。这种类型会丢失。这是我们的代码:
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");
}
}
我们还编写了一个自动测试来揭示问题。这是测试。
@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()
);
}
此测试的结果是针对此代码运行的:
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}]>
在反序列化时,有没有办法不丢失列表中的参数化类型?谢谢!
答案 0 :(得分:1)
根据我们在评论中的交流,问题是使用new ParameterizedTypeReference<T>() {}
。这不起作用的原因与这些类型的标记/类型引用/等有关。对象工作。 (最初在Neal Gafter的博客文章here中有描述。)
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.
}
}
因此,如果您有new ParameterizedTypeReference<T>() {}
,那么getClass().getGenericSuperclass()
将返回隐式扩展子句中的类型,即ParameterizedTypeReference<T>
,其类型参数是类型变量T
。< / p>
如果使用此ParameterizedTypeReference
对于让测试工作至关重要,那么不幸的是,你必须将其作为参数传递。必须使用实际类型参数new ParameterizedTypeReference<List<UserValueDTO>>() {}
创建它。
Guava TypeToken
有一些花哨的功能来解析在类上声明的类型变量,但对方法上声明的类型变量执行相同操作实际上是不可能的,因为无法将类型参数重新声明为呼叫站点的方法。