1. 概述
在本篇文章中,我们来深入分析 Spring 框架中的一个常见异常:HttpMessageNotWritableException: "No converter found for return value of type"
。
首先会说明这个异常的主要成因,然后通过一个实际的示例复现它,最后给出解决方案。
2. 异常成因
通常来说,这个异常会在 Spring 无法获取返回对象的属性时抛出。
最常见的原因是:返回的对象没有为它的属性提供任何 public
的 getter 方法。
Spring Boot 默认使用 Jackson 库来完成请求和响应对象的序列化/反序列化工作。
因此,另一个常见的原因是:缺少 Jackson 相关依赖,或者依赖版本不正确。
✅ 总结一下,遇到这类问题时应检查以下几点:
- 是否有默认构造函数
- 是否有对应的 getter 方法
- Jackson 依赖是否正确引入
⚠️ 注意:该异常类型从 java.lang.IllegalArgumentException
被改为 org.springframework.http.converter.HttpMessageNotWritableException
,详见 Spring 官方提交记录。
3. 实战复现
下面通过一个实际例子来复现 HttpMessageNotWritableException
异常。
我们创建一个基于 Spring Boot 的简单学生管理 REST API。
3.1 创建模型类(缺少 getter)
public class Student {
private int id;
private String firstName;
private String lastName;
private String grade;
public Student() {
}
public Student(int id, String firstName, String lastName, String grade) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.grade = grade;
}
// Setters...
}
可以看到,我们故意没有添加任何 getter 方法。
3.2 创建控制器
@RestController
@RequestMapping(value = "/api")
public class StudentRestController {
@GetMapping("/student/{id}")
public ResponseEntity<Student> get(@PathVariable("id") int id) {
return ResponseEntity.ok(new Student(id, "John", "Williams", "AA"));
}
}
3.3 发起请求
使用 CURL 请求:
curl http://localhost:8080/api/student/1
返回结果:
{
"timestamp":"2021-02-14T14:54:19.426+00:00",
"status":500,
"error":"Internal Server Error",
"message":"",
"path":"/api/student/1"
}
3.4 查看日志
控制台输出异常信息:
org.springframework.http.converter.HttpMessageNotWritableException:
No converter found for return value of type: class com.baeldung.boot.noconverterfound.model.Student
3.5 编写测试验证
@RunWith(SpringRunner.class)
@WebMvcTest(StudentRestController.class)
public class NoConverterFoundIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Test
public void whenGettersNotDefined_thenThrowException() throws Exception {
String url = "/api/student/1";
this.mockMvc.perform(get(url))
.andExpect(status().isInternalServerError())
.andExpect(result -> assertThat(result.getResolvedException())
.isInstanceOf(HttpMessageNotWritableException.class))
.andExpect(result -> assertThat(result.getResolvedException().getMessage())
.contains("No converter found for return value of type"));
}
}
❌ 测试通过,说明确实因为缺少 getter 导致异常。
4. 解决方案
最常见且推荐的做法是:为每个需要返回的属性添加 public
的 getter 方法。
✅ 修改后的 Student
类如下:
public class Student {
private int id;
private String firstName;
private String lastName;
private String grade;
public Student() {
}
public Student(int id, String firstName, String lastName, String grade) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.grade = grade;
}
// Getters
public int getId() { return id; }
public String getFirstName() { return firstName; }
public String getLastName() { return lastName; }
public String getGrade() { return grade; }
// Setters...
}
然后添加一个测试用例验证修复是否生效:
@Test
public void whenGettersAreDefined_thenReturnObject() throws Exception {
String url = "/api/student/2";
this.mockMvc.perform(get(url))
.andExpect(status().isOk())
.andExpect(jsonPath("$.firstName").value("John"));
}
✅ 测试通过,问题解决。
⚠️ 踩坑提醒:有人可能会直接把字段设为 public
来绕过问题,但这不是推荐做法,破坏了封装性,也不符合 Java Bean 规范。
5. 小结
这篇文章我们讲解了 Spring 中抛出 HttpMessageNotWritableException
的常见原因,即:
- 缺少 getter 方法
- Jackson 依赖缺失或配置错误
并通过一个完整的示例演示了如何复现和修复这个问题。
👉 完整代码可参考 GitHub 示例仓库:Spring Boot Data 示例。