1. 简介

RestTemplate 是 Spring 中用于执行客户端 HTTP 请求的核心工具类。它提供了丰富的便捷方法,帮助我们快速构建 HTTP 请求并处理响应。

得益于与 Jackson 的无缝集成,RestTemplate 能够轻松地将大多数 Java 对象序列化为 JSON,或从 JSON 反序列化回来——几乎无需额外配置。⚠️ 但有一个常见坑点:处理对象列表时并不像单个对象那样简单直接

本文将带你掌握如何使用 RestTemplate 正确地 GET(获取)和 POST(提交)对象列表,避开类型擦除带来的陷阱。


2. 示例服务

我们假设有一个员工管理 API,提供两个核心接口:

  • GET /employees:获取所有员工列表
  • POST /employees:批量创建员工

客户端与服务端之间通过一个简单的 DTO 传输数据:

public class Employee {
    public long id;
    public String title;

    // 标准构造函数、getter/setter 省略
}

接下来,我们就用 RestTemplate 实现对 Employee 列表的获取和提交。


3. 使用 RestTemplate 获取对象列表

通常情况下,发起 GET 请求可以使用 RestTemplate 提供的简化方法,比如:

getForObject(URI url, Class<T> responseType)

该方法会向指定 URI 发起 GET 请求,并自动将响应体转换为指定的 Java 类型。✅ 对普通 POJO 非常好用。

❌ 但问题来了:它无法直接处理 List<Employee> 这样的泛型集合类型

❓ 为什么不行?

根本原因在于 Java 的类型擦除(Type Erasure)。运行时 JVM 无法知道 List<Employee> 中的 Employee 具体是什么类型,因此 Jackson 无法正确反序列化每个元素。

✅ 解决方案一:使用数组

最简单粗暴的方式是——用数组代替 List。数组是带有具体类型的,不会受类型擦除影响。

我们可以使用 getForEntity() 方法:

ResponseEntity<Employee[]> response =
  restTemplate.getForEntity(
    "http://localhost:8080/employees/",
    Employee[].class);

Employee[] employees = response.getBody();

拿到数组后,如果需要 List,可以用 Arrays.asList() 转换。

💡 提示:你也可以用 exchange() 实现相同效果,灵活性更高。底层真正干活的是 ResponseExtractor,如需深度定制,可调用 execute() 并传入自定义实现。

✅ 解决方案二:使用包装类(Wrapper Class)

很多 REST API 返回的并不是裸列表,而是带有一个外层包装对象,例如:

{
  "employees": [
    { "id": 1, "title": "Developer" },
    { "id": 2, "title": "Manager" }
  ]
}

针对这种情况,我们可以定义一个包装类:

public class EmployeeList {
    private List<Employee> employees;

    public EmployeeList() {
        employees = new ArrayList<>();
    }

    // getter / setter
    public List<Employee> getEmployees() {
        return employees;
    }

    public void setEmployees(List<Employee> employees) {
        this.employees = employees;
    }
}

然后就可以愉快地使用 getForObject() 了:

EmployeeList response = restTemplate.getForObject(
  "http://localhost:8080/employees",
  EmployeeList.class);

List<Employee> employees = response.getEmployees();

✅ 代码简洁清晰,✅ 类型安全,❌ 缺点是多了一个包装类,略显冗余。


4. 使用 RestTemplate 提交对象列表

POST 场景比 GET 简单得多。RestTemplate 提供了 postForObject() 方法:

postForObject(URI url, Object request, Class<T> responseType)

它会向指定 URI 发起 POST 请求,携带请求体,并将响应转换为目标类型。

⚠️ 关键区别:POST 时不需要担心类型擦除问题

因为此时是 Java 对象 → JSON 的序列化过程,JVM 完全知道 List 中每个对象的类型,Jackson 可以正确生成 JSON。

示例:直接提交 List

List<Employee> newEmployees = new ArrayList<>();
newEmployees.add(new Employee(3, "Intern"));
newEmployees.add(new Employee(4, "CEO"));

restTemplate.postForObject(
  "http://localhost:8080/employees/",
  newEmployees,
  ResponseEntity.class);

✅ 简单直接,无需额外包装。

示例:使用包装类提交(保持一致性)

如果你的 API 设计要求请求体也必须包装,比如:

{ "employees": [ ... ] }

那也很容易处理:

List<Employee> newEmployees = new ArrayList<>();
newEmployees.add(new Employee(3, "Intern"));
newEmployees.add(new Employee(4, "CEO"));

restTemplate.postForObject(
  "http://localhost:8080/employees",
  new EmployeeList(newEmployees),
  ResponseEntity.class);

只要你的 EmployeeList 类有合适的构造函数或 setter,Jackson 就能正确序列化。


5. 总结

RestTemplate 是构建 HTTP 客户端的利器,对常规对象操作非常友好。但在处理泛型集合时,由于 Java 类型擦除的限制,需要我们稍作变通。

✅ 正确姿势总结:

场景 推荐方案
GET List 使用数组 T[] 或包装类
POST List 直接传 List<T>,无需担心类型擦除
API 返回包装结构 统一使用 Wrapper Class,保持一致性

记住:GET 要防反序列化失败,POST 通常没问题

本文完整代码示例可在 GitHub 查看:spring-resttemplate-1


原始标题:Get and Post Lists of Objects with RestTemplate