列表返回类型上具有休息模板的通用方法?

时间:2017-06-07 15:28:27

标签: java spring spring-boot

我试图创建一个通用的休息模板方法。目标是反序列化为泛型类型,即使类型已参数化。不幸的是,当我尝试反序列化时,我得到一个哈希映射列表或类似的东西。这种类型会丢失。这是我们的代码:

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}]>

在反序列化时,有没有办法不丢失列表中的参数化类型?谢谢!

1 个答案:

答案 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有一些花哨的功能来解析在类上声明的类型变量,但对方法上声明的类型变量执行相同操作实际上是不可能的,因为无法将类型参数重新声明为呼叫站点的方法。