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 示例


原始标题:“HttpMessageNotWritableException: No converter found for return value of type” | Baeldung