java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Spring MVC @ResponseBody返回值写入响应体

Spring MVC使用@ResponseBody注解把返回值直接写入响应体

作者:步行cgn

本文深入解析SpringMVC的@ResponseBody注解,包括其作用、原理、与@RestController的区别、常见问题及解决方法,你将学会如何让方法返回值直接写入HTTP响应体,返回JSON或字符串,并理解转换器选择机制,读完能帮你快速解决404、406中文乱码等常见错误,需要的朋友可以参考下

一、它是什么

@ResponseBody 是 Spring MVC 提供的注解,作用是告诉 Spring MVC:这个方法的返回值不要交给视图解析器处理,直接写入 HTTP 响应体。

不加 @ResponseBody 时,Controller 方法返回 String,Spring MVC 会把它当作视图名,交给 ViewResolver 去找模板:

@Controller
public class PageController {

    @GetMapping("/hello")
    public String hello() {
        return "hello";  // 找 hello.html 模板
    }
}

加了 @ResponseBody 后,同一个返回值变成响应体内容:

@Controller
public class TextController {

    @GetMapping("/hello")
    @ResponseBody
    public String hello() {
        return "hello";  // 响应体就是 hello
    }
}

一句话概括:@ResponseBody 把“返回视图名”切换成“返回响应体数据”。

二、它解决什么问题

传统 Spring MVC 中,Controller 的职责是“准备数据 + 选择视图”,最终由 JSP、Thymeleaf 渲染页面。

但在前后端分离或 RESTful 接口场景下,后端只需要返回数据,不需要渲染页面。@ResponseBody 就是为这种场景准备的:

它把 Controller 从“视图渲染”中解放出来,让方法返回值直接成为 HTTP 响应内容。

三、底层工作原理

1. 处理链路

请求到达 DispatcherServlet
  → 匹配 HandlerMethod
  → RequestMappingHandlerAdapter 调用方法
  → 方法返回对象
  → RequestResponseBodyMethodProcessor 处理返回值
      → 遍历 HttpMessageConverter
      → 找到能处理返回值类型的转换器
      → 调用 write() 写入响应体
  → 不再调用 ViewResolver

关键类是 RequestResponseBodyMethodProcessor,它同时负责读取 @RequestBody 参数和写出 @ResponseBody 返回值。

2. 判断条件

RequestResponseBodyMethodProcessor.supportsReturnType() 的判断逻辑:

@Override
public boolean supportsReturnType(MethodParameter returnType) {
    return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
            returnType.hasMethodAnnotation(ResponseBody.class));
}

只要方法或类上有 @ResponseBody,就由它处理返回值。

3. 选择转换器

返回对象后,AbstractMessageConverterMethodProcessor.writeWithMessageConverters() 会:

  1. 获取返回值的实际类型。
  2. 获取请求的 Accept 头。
  3. 遍历所有 HttpMessageConverter。
  4. 调用 canWrite(返回类型, 媒体类型) 找到匹配的转换器。
  5. 调用 write() 把对象写入响应体,并设置 Content-Type。

常见转换器对应关系:

返回类型转换器Content-Type
StringStringHttpMessageConvertertext/plain
普通对象MappingJackson2HttpMessageConverterapplication/json
byte[]ByteArrayHttpMessageConverterapplication/octet-stream
ResourceResourceHttpMessageConverter文件对应的类型

4. 用图串起来

Controller 返回对象
  │
  ▼
RequestResponseBodyMethodProcessor
  │
  ├── 没有 @ResponseBody → 交给 ViewResolver 处理
  │
  └── 有 @ResponseBody
        │
        ▼
      遍历 HttpMessageConverter
        │
        ▼
      找到匹配的转换器,调用 write()
        │
        ▼
      写入响应体,设置 Content-Type

四、@ResponseBody 和 @RestController

@RestController 是 @Controller 和 @ResponseBody 的组合注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
}

因此:

@RestController
public class UserController {
}

等价于:

@Controller
@ResponseBody
public class UserController {
}

类上加了 @RestController,类中所有方法都隐含 @ResponseBody,不需要逐个方法标注。

对比@Controller@RestController
返回值处理默认返回视图名直接写响应体
是否隐含 @ResponseBody否是
典型用途页面渲染RESTful JSON 接口

五、代码示例

1. 返回 JSON 对象

@RestController
@RequestMapping("/api/user")
public class UserController {

    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.findById(id);
    }
}

返回 User 对象,Jackson 转换器把它序列化成 JSON:

{"id":1,"username":"jack","email":"jack@example.com"}

2. 返回字符串

@GetMapping("/text")
public String text() {
    return "hello";
}

StringHttpMessageConverter 优先级高于 Jackson,响应体是纯文本:

hello

不是 "hello"。如果希望返回 JSON 字符串,需要声明 produces = "application/json":

@GetMapping(value = "/json-string", produces = "application/json")
public String jsonString() {
    return "hello";
}

输出:

"hello"

3. 配合 ResponseEntity

@GetMapping("/user/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
    User user = userService.findById(id);
    if (user == null) {
        return ResponseEntity.notFound().build();
    }
    return ResponseEntity.ok(user);
}

ResponseEntity 携带状态码、响应头和响应体。响应体部分仍然由 HttpMessageConverter 写出。

4. 混合使用 @Controller 和 @ResponseBody

@Controller
public class MixedController {

    @GetMapping("/page")
    public String page() {
        return "index";  // 返回视图名
    }

    @GetMapping("/data")
    @ResponseBody
    public User data() {
        return new User(1L, "jack");  // 返回 JSON
    }
}

同一个类中,没有 @ResponseBody 的方法走视图解析,有 @ResponseBody 的方法直接写响应体。

六、和 @RequestBody 的区别

对比@ResponseBody@RequestBody
方向出:Java 对象 → 响应体入:请求体 → Java 对象
位置方法或类上方法参数上
依赖组件HttpMessageConverter.write()HttpMessageConverter.read()
典型场景返回 JSON接收 JSON

一句话记忆:@RequestBody 读进来,@ResponseBody 写出去。

七、常见问题

1. 返回对象变成 404 或模板找不到

原因:使用了 @Controller 但没有加 @ResponseBody,Spring MVC 把返回值当视图名去找模板。

@Controller
public class UserController {

    @GetMapping("/user")
    public User getUser() {
        return new User();  // 会被当作视图名,报 TemplateInputException
    }
}

解决:加 @ResponseBody,或改用 @RestController。

2. 返回 String 没有双引号

这是正常行为。StringHttpMessageConverter 优先级高于 Jackson,字符串被当作纯文本。需要 JSON 字符串时声明 produces = "application/json"。

3. 406 Not Acceptable

客户端 Accept 头不接受任何转换器支持的媒体类型。比如接口只支持 JSON,客户端却发 Accept: text/html。

解决:检查客户端 Accept 头,或后端用 produces 声明响应类型。

4. 415 和 @ResponseBody 无关

415 是读请求体失败,通常和 @RequestBody 或 Content-Type 有关。@ResponseBody 只负责写响应体。

5. 中文乱码

返回 String 时,StringHttpMessageConverter 默认字符集是 ISO_8859_1,中文可能乱码。解决:

@GetMapping(value = "/text", produces = "text/plain;charset=UTF-8")

或者配置 StringHttpMessageConverter 的默认字符集为 UTF-8。

6. 返回值是 void

方法返回 void 且没有手动写响应体时,@ResponseBody 不会触发任何转换。需要手动通过 HttpServletResponse 写出。

八、记忆方法

1. 一句话定位

有 ResponseBody,返回值直接进响应体;
没有 ResponseBody,返回值当视图名。

2. 两个组合

@Controller + @ResponseBody = @RestController
@ResponseBody 写出,@RequestBody 读入

3. 处理链路口诀

返回值 → RequestResponseBodyMethodProcessor → 遍历转换器 → write → 响应体

4. 转换器优先级口诀

String 在前,Jackson 在后,
返回字符串纯文本,
返回对象才走 JSON。

5. 常见错误口诀

对象变 404,多半忘了 ResponseBody;
String 没引号,StringHttpMessageConverter 正常行为;
406 是 Accept 不匹配。

九、总结

维度说明
作用方法返回值直接写入 HTTP 响应体,不经过视图解析器
位置方法或类上
核心处理器RequestResponseBodyMethodProcessor
依赖组件HttpMessageConverter
组合注解@RestController = @Controller + @ResponseBody
与 @RequestBody 区别一个写出,一个读入
常见问题忘加注解导致 404、返回 String 无引号、406、中文乱码

@ResponseBody 是 Spring MVC 中“返回值不渲染页面、直接输出数据”的开关。理解它,就能解释为什么 @RestController 的方法能直接返回 JSON、为什么 @Controller 方法返回对象会去找模板、为什么返回 String 没有双引号。记住“有它写响应体、没它找视图名”,再记住“String 优先、对象走 Jackson”,这类返回值处理问题就能快速判断。

以上就是Spring MVC使用@ResponseBody注解把返回值直接写入响应体的详细内容,更多关于Spring MVC @ResponseBody返回值写入响应体的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文