RestTemplate 处理泛型对象的反序列化问题

项目开发   2024-07-24 16:26   932   0  

在编写单测时,经常需要使用 RestTemplate 来发送 HTTP 请求并接收响应,然后对返回的数据进行判断和处理。某次在写测试用例返回了一个 List 对象,在遍历 List 对象时报错” java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class xxx。实际上,当涉及到处理泛型集合对象(如 List<Dashboard>Page<Dashboard>)时,反序列化过程可能会遇到一些问题。

1. 类型擦除机制

Java 在编译时会执行类型擦除(Type Erasure),这意味着所有泛型类型信息在编译时都会被移除。例如,List<Dashboard> 在运行时实际上只是一个 List。这种类型擦除机制是为了兼容 Java 的早期版本,但它也带来了一个问题:在运行时,我们无法直接获取泛型类型的信息。

List<Dashboard> dashboards = new ArrayList<>();
Class<?> clazz = dashboards.getClass();
System.out.println(clazz); // 输出:class java.util.ArrayList

在上面的示例中,运行时只能看到 ArrayList,而无法知道它实际包含的泛型类型 Dashboard

2. Jackson 反序列化

Spring Boot 使用 Jackson 作为默认的 JSON 处理库。Jackson 在反序列化 JSON 时需要知道目标类型的确切信息。对于非泛型类型(如 Dashboard),Jackson 可以直接从类信息中获取反序列化所需的类型信息:

ObjectMapper mapper = new ObjectMapper();Dashboard dashboard = mapper.readValue(json, Dashboard.class);

但是,对于泛型类型(如 List<Dashboard>),由于类型擦除的原因,Jackson 无法直接获取泛型类型信息,因此需要额外的类型信息:

ObjectMapper mapper = new ObjectMapper();
List<Dashboard> dashboards = mapper.readValue(json, new TypeReference<List<Dashboard>>() {});

TypeReference 是 Jackson 提供的一种解决方案,用于在运行时提供泛型类型信息。

3. RestTemplate 处理泛型集合对象

在使用 RestTemplate 时,我们遇到的反序列化问题本质上与 Jackson 一样。当我们调用 restTemplate.getForObject(url, List.class) 时,RestTemplate 无法知道 List 的具体类型,因此无法正确反序列化为 List<Dashboard>

为了正确处理泛型集合对象,我们需要使用 ParameterizedTypeReference 提供泛型类型信息:

ResponseEntity<List<Dashboard>> response = restTemplate.exchange(
    url,
    HttpMethod.GET,
    null,
    new ParameterizedTypeReference<List<Dashboard>>() {}
);
List<Dashboard> dashboards = response.getBody();

ParameterizedTypeReference 是 Spring 提供的一个解决方案,用于在运行时提供泛型类型信息,类似于 Jackson 的 TypeReference

4. 代码示例

下面是一个完整的示例,展示如何使用 RestTemplate 处理泛型集合对象 List<Dashboard> 和分页对象 Page<Dashboard> 的反序列化。

4.1 处理 List<Dashboard>

import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@Service
public class DashboardService {

    @Autowired
    private RestTemplate restTemplate;

    public List<Dashboard> getAllDashboards() {
        String url = "http://example.com/api/dashboards";
        ResponseEntity<List<Dashboard>> response = restTemplate.exchange(
                url,
                HttpMethod.GET,
                null,
                new ParameterizedTypeReference<List<Dashboard>>() {}
        );
        return response.getBody();
    }
}

4.2 处理 Page<Dashboard>

import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class DashboardService {

    @Autowired
    private RestTemplate restTemplate;

    public PageResponse<Dashboard> getDashboardsPage(int page, int size) {
        String url = String.format("http://example.com/api/dashboards?page=%d&size=%d", page, size);
        ResponseEntity<PageResponse<Dashboard>> response = restTemplate.exchange(
                url,
                HttpMethod.GET,
                null,
                new ParameterizedTypeReference<PageResponse<Dashboard>>() {}
        );
        return response.getBody();
    }
}

5. 泛型擦除可能带来的其他相关问题

  1. 类型安全性问题

        由于泛型类型信息在编译期存在但运行期被擦除,那么一些类型检查就无法在运行期完成,可能导致类型不安全。

  1. 反射获取类型问题

        通过反射无法获取参数化类型,只能获取原始类型,List而无法获取List<String>

  1. 序列化问题

        无法通过原始类信息重新构造出参数化类型实例,影响对象的序列化和反序列化。

  1. 泛型数组创建问题

        由于类型擦除,无法创建参数化类型的数组,如不能定义List<String>[]数组。

  1. 泛型转型问题

        由于类型安全检查依赖于类型信息,类型擦除后就无法进行参数化类型之间的正确转型操作。

  1. 泛型继承问题

        参数化类型的子类不能识别其父类原始类型的参数,带来一些继承限制。

  1. 泛型边界限制问题

        泛型类型上带有范围限定<? extends T><? super T>也会因为类型擦除而丧失部分约束能力。

  1. 泛型特化问题

        无法在运行期将generic类型具体化为其子类型。

    所以总体来说,泛型擦除令Java泛型在一定程度上丧失了类型安全的完全性。

6. 总结

Java 的类型擦除机制导致在运行时无法直接获取泛型类型信息,这会影响到 RestTemplate 和 Jackson 在反序列化泛型集合对象时的行为。通过使用 ParameterizedTypeReference 或 Jackson 的 TypeReference,我们可以在运行时提供泛型类型信息,从而正确反序列化泛型集合对象。


上一篇
没有了
博客评论
还没有人评论,赶紧抢个沙发~
发表评论
说明:请文明发言,共建和谐网络,您的个人信息不会被公开显示。