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