SpringMVC
- MVC
-
- [什么是 MVC?](#什么是 MVC?)
-
- [1. Model(模型)](#1. Model(模型))
- [2. View(视图)](#2. View(视图))
- [3. Controller(控制器)](#3. Controller(控制器))
- [Spring MVC 中的 MVC 实现](#Spring MVC 中的 MVC 实现)
-
- 核心组件
- [Spring MVC 的工作流程](#Spring MVC 的工作流程)
- [MVC 的优势](#MVC 的优势)
- [Spring MVC 的扩展](#Spring MVC 的扩展)
- 建立请求连接
-
- 单一路径映射
- 组合映射:类级别与方法级别
- 限定请求方法
-
- [method 属性](#method 属性)
-
- [1. 只支持 GET](#1. 只支持 GET)
- [2. 只支持 POST](#2. 只支持 POST)
- [3. 同时支持 GET 和 POST](#3. 同时支持 GET 和 POST)
- 派生注解简化方法映射
- 请求处理
-
- [查询参数 : @RequestParam](#查询参数 : @RequestParam)
-
- [@RequestParam 对空字符串和默认值的独特处理规则](#@RequestParam 对空字符串和默认值的独特处理规则)
- 路径变量:@PathVariable
- [请求体(JSON / XML):@RequestBody](#请求体(JSON / XML):@RequestBody)
- 表单参数与文件上传
- 数组和集合上传
-
- 传递数组上传
- 集合(List)上传
- [JSON 数组(通过 @RequestBody)上传](#JSON 数组(通过 @RequestBody)上传)
- [请求头与 Cookie:@RequestHeader、@CookieValue](#请求头与 Cookie:@RequestHeader、@CookieValue)
- [参数解析流程(Mermaid 图)](#参数解析流程(Mermaid 图))
- 响应返回
-
- 返回普通字符串
- [返回 JSON 对象](#返回 JSON 对象)
- 设置响应状态码
-
- [方式一:使用 @ResponseStatus 注解](#方式一:使用 @ResponseStatus 注解)
- [方式二:通过 HttpServletResponse 手动设置](#方式二:通过 HttpServletResponse 手动设置)
- 两种方式对比
- 返回HTML页面:@Controller
-
- 准备工作
- [使用 @Controller 返回页面](#使用 @Controller 返回页面)
- [@Controller + @ResponseBody 返回数据](#@Controller + @ResponseBody 返回数据)
- [@Controller 与 @RestController 的区别](#@Controller 与 @RestController 的区别)
- 设置Content-Type
-
- [通过 produces 属性设置](#通过 produces 属性设置)
- [通过 HttpServletResponse 手动设置](#通过 HttpServletResponse 手动设置)
- [常见 Content-Type 取值](#常见 Content-Type 取值)
- 两种方式对比
- 设置Header
-
- [方式一:通过 HttpServletResponse 手动设置](#方式一:通过 HttpServletResponse 手动设置)
- [方式二:通过 @ResponseHeader 注解(Spring 6.0+)](#方式二:通过 @ResponseHeader 注解(Spring 6.0+))
- [方式三:通过 ResponseEntity 设置](#方式三:通过 ResponseEntity 设置)
- [常见 Header 设置场景](#常见 Header 设置场景)
- 三种方式对比
MVC
什么是 MVC?
MVC(Model-View-Controller)是一种经典的软件设计模式,它将应用程序分为三个核心组件:
Model View Controller Model View Controller 1. 请求 2. 业务处理 3. 返回结果 4. 响应
1. Model(模型)
- 职责:处理业务逻辑和数据操作
- 特点 :
- 包含应用程序的数据和业务规则
- 独立于用户界面
- 负责数据的存取、验证和处理
- 示例 :在 Spring MVC 中,Model 可以是实体类(如
User)、Service 层、DAO 层等
2. View(视图)
- 职责:负责数据的展示
- 特点 :
- 将 Model 中的数据以特定格式呈现给用户
- 不包含业务逻辑
- 只负责显示数据
- 示例:HTML 页面、JSP、Thymeleaf 模板、JSON 响应等
3. Controller(控制器)
- 职责:接收用户输入,协调 Model 和 View
- 特点 :
- 处理用户请求
- 调用 Model 处理业务逻辑
- 选择合适的 View 进行渲染
- 示例 :Spring MVC 中的
@Controller或@RestController注解的类
Spring MVC 中的 MVC 实现
核心组件
MVC 三层
客户端请求
DispatcherServlet
HandlerMapping
Controller
接收请求、协调
Model
业务逻辑、数据
返回 ModelAndView 或数据
ViewResolver
View
数据展示
返回响应
Spring MVC 的工作流程
- DispatcherServlet:前端控制器,接收所有 HTTP 请求
- HandlerMapping:根据请求 URL 找到对应的 Controller 方法
- Controller:处理请求,调用业务逻辑,返回结果
- ViewResolver:解析视图名称,找到对应的视图模板
- View:渲染最终结果,返回给客户端
MVC 的优势
- 分离关注点:业务逻辑、数据展示、用户输入处理分离
- 代码复用:Model 可以在多个 View 中复用
- 易于维护:各组件职责清晰,修改影响范围小
- 并行开发:前端和后端可以独立开发
Spring MVC 的扩展
除了传统的 MVC,Spring 还支持:
- RESTful API :通过
@RestController实现 - WebFlux:响应式编程模型
- WebSocket:实时双向通信
- 文件上传 :
MultipartFile处理
通过理解 MVC 设计模式,可以更好地掌握 Spring MVC 的工作原理,编写出结构清晰、易于维护的 Web 应用程序。# 整体流程概览:从请求到响应
Spring MVC 处理请求的核心入口是 DispatcherServlet。它拦截所有 HTTP 请求,然后通过 处理器映射(Handler Mapping) 来查找对应的处理方法。@RequestMapping 正是定义映射规则的关键注解,而 @RestController 则决定了方法返回值如何写入响应体。下面的流程图展示了这一完整过程:
是
否
客户端HTTP请求
DispatcherServlet
前端控制器
HandlerMapping
根据注解查找处理器
匹配到
对应的Handler?
执行Controller方法
如@RestController方
返回404 Not Found
方法返回对象/字符串
HttpMessageConverter
将返回值序列化
(对象→JSON等)
返回HTTP响应给客户端
@RestController标记的类会被 Spring 自动扫描并注册为 Bean,成为可以处理请求的控制器。@RequestMapping负责将特定的 URL 路径与控制器中的方法绑定,HandlerMapping 正是依赖这些注解来找到对应处理器。- 整个流程简单来讲:
DispatcherServlet借助@RequestMapping的映射找到@RestController中的方法,执行后直接将返回值序列化到响应体。
建立请求连接
单一路径映射
最简单的场景:请求一个路径,返回一个字符串。
java
@RestController
public class UserController {
@RequestMapping("/user")
public String getUser() {
return "user info";
}
}
访问 :GET /user → 响应体为 user info
关键点:
@RestController=@Controller+@ResponseBody。@Controller将类标记为 Spring MVC 的控制器。@ResponseBody表示该类中所有方法的返回值都直接写入 HTTP 响应体(通常转换为 JSON 或纯文本),而不是跳转到视图(如 JSP)。
- 如果忘记写
@RestController,Spring 不会把该类作为请求处理器,自然无法访问。 - 方法的返回值
"user info"会原样写回客户端,不需要我们手动操作HttpServletResponse。
组合映射:类级别与方法级别
当接口变多时,将路径拆分成类级别的"命名空间"和方法级别的子路径是最佳实践。
java
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/m1")
public String m1() {
return "m1";
}
@RequestMapping("/m2")
public String m2() {
return "m2";
}
}
- 访问
/user/m1→ 返回"m1" - 访问
/user/m2→ 返回"m2"
路径规则 :最终路径 = 类上的 @RequestMapping 路径 + 方法上的 @RequestMapping 路径。
如果类或方法上的路径以 / 开头,Spring 会自动规范化,确保拼接结果正确,因此写 @RequestMapping("/m1") 和 @RequestMapping("m1") 效果相同。
限定请求方法
method 属性
java
@RestController
@RequestMapping("/user")
public class UserController {
// 既支持 GET,又支持 POST
@RequestMapping("/m1")
public String m1() {
return "m1";
}
}
- 访问
/user/m1,无论使用 GET 还是 POST 方式,都能成功返回"m1"。 - 原因:
@RequestMapping默认method = {}(空数组),表示接受所有 HTTP 方法。
通过 method 属性可以指定一个或多个支持的 HTTP 方法。
1. 只支持 GET
java
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping(value = "/m2", method = RequestMethod.GET)
public String m2() {
return "m2 (GET only)";
}
}
如果使用 POST 请求 /user/m2,服务器将返回 405 Method Not Allowed 。
HTTP响应常见状态码
2. 只支持 POST
java
@RequestMapping(value = "/m3", method = RequestMethod.POST)
public String m3() {
return "m3 (POST only)";
}
如果使用GET请求 /user/m3,服务器将返回 405 Method Not Allowed 。
HTTP响应常见状态码
3. 同时支持 GET 和 POST
java
@RequestMapping(value = "/m4", method = {RequestMethod.GET, RequestMethod.POST})
public String m4() {
return "m4 (GET+POST)";
}
⚠️ 注意:同一个 URL 路径,如果既写了 GET 方法又写了 POST 方法(方法名相同)会冲突。Spring 允许重载,但方法参数必须不同,实际极少这样使用。推荐不同业务逻辑使用不同路径或用派生注解区分。
java// 错误示例:方法签名冲突 @RequestMapping(value = "/same", method = GET) public String handle() { ... } @RequestMapping(value = "/same", method = POST) public String handle() { ... } // 编译错误:方法名相同且参数相同解决:使用不同方法名,或使用不同路径,或者利用请求参数进行区分(不推荐)。
派生注解简化方法映射
Spring 4.3 开始提供了专门针对常见 HTTP 方法的注解,它们是 @RequestMapping 的特化版本。
| 注解 | 等效写法 |
|---|---|
@GetMapping |
@RequestMapping(method = RequestMethod.GET) |
@PostMapping |
@RequestMapping(method = RequestMethod.POST) |
@PutMapping |
@RequestMapping(method = RequestMethod.PUT) |
@DeleteMapping |
@RequestMapping(method = RequestMethod.DELETE) |
@PatchMapping |
@RequestMapping(method = RequestMethod.PATCH) |
使用示例
java
@RestController
@RequestMapping("/user")
public class UserController {
// 等价于 @RequestMapping(value = "/get", method = GET)
@GetMapping("/get")
public String get() {
return "GET request";
}
// 等价于 @RequestMapping(value = "/post", method = POST)
@PostMapping("/post")
public String post() {
return "POST request";
}
@PutMapping("/put")
public String put() {
return "PUT request";
}
@DeleteMapping("/delete")
public String delete() {
return "DELETE request";
}
}
优点:
- 代码更短、语义更清晰。
- 减少拼写错误(不用再写
RequestMethod.GET)。 - 更容易被 IDE 和静态检查工具识别。
请求处理
在 Spring MVC 中,控制器可以通过多种注解灵活接收客户端传递的参数,包括查询参数、路径变量、请求体、表单字段、请求头等。下面逐一说明,并给出推荐用法。
查询参数 : @RequestParam
适用于 URL 问号后的参数(?key=value),常用于 GET 请求。
java
@RestController
@RequestMapping("/user")
public class UserController {
/**
* @RequestParam 用于绑定 URL 问号后的查询参数(?key=value),常见于 GET 请求。
*
* 默认必传,可通过 {@code required = false} 设为可选;
* 使用 {@code defaultValue} 可指定缺省值,避免空值问题,此时 {@code required} 自动视为 false;
* 若请求参数名与方法形参名一致(且编译时保留了参数名,如使用 -parameters),可省略注解中的名称;
* 否则建议显式指定 {@code value} 属性,以保证清晰与兼容。
*
*/
@GetMapping("/get")
public String getUser(@RequestParam("n") String name, // 请求参数 "n",绑定到形参 name,必传
@RequestParam(value = "age", defaultValue = "0") int age, // 可选,默认0
@RequestParam(value = "email", required = false) String email) { // 可选,无值时为 null
return "name: " + name + ", age: " + age + ", email: " + email;
}
}
测试结果 :


@RequestParam 对空字符串和默认值的独特处理规则
text
http://127.0.0.1:8080/user/get?n=&age=&email=
会发现不报错,明明@RequestParam("n") String name的意思是必须传入一个值

空字符串""对于string也算值
参数 email 的处理
rawValue = ''
不为 null
无 defaultValue
required=false
直接绑定空字符串
email = ''
参数 age 的处理
rawValue = ''
不为 null
值空 且 有 defaultValue='0'
触发默认值回退
使用默认值
age = 0
参数 n 的处理
rawValue = ''
不为 null
无 defaultValue
required=true(默认)
直接绑定空字符串
name = ''
最终输出: name: , age: 0, email:
java
@GetMapping("/getTest")
public String getUserTest(@RequestParam("n") String name,
@RequestParam int age,//遇到空字符串类型转换失败,报 400 错误
@RequestParam(value = "email", required = false) String email) {
return "name: " + name + ", age: " + age + ", email: " + email;
}

参数 email 的处理
是
rawValue=''
存在?
required=false, 无 defaultValue
绑定为 ''(但 age 已中断)
参数 age 的处理
是
rawValue=''
存在?
无 defaultValue
尝试转换为 int
TypeMismatchException
❌ 返回 400 错误
参数 n 的处理
是
rawValue=''
存在?
无 defaultValue
绑定为 ''
最终响应
400 Bad Request
(因为 age 转换失败)
路径变量:@PathVariable
RESTful 风格将参数直接放在 URL 路径中。
java
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/{id}")
public String getUserById(@PathVariable int id) {
return "user id = " + id;
}
// 多路径变量
@GetMapping("/{userId}/orders/{orderId}")
public String getOrder(@PathVariable int userId,
@PathVariable int orderId) {
return "userId=" + userId + ", orderId=" + orderId;
}
}
- 占位符
{变量名}必须与方法参数名一致,否则需用@PathVariable("name")显式指定。 - 路径变量是 URL 的一部分,通常用于标识资源。
测试结果 :



请求体(JSON / XML):@RequestBody
适用于 POST、PUT 等请求,将请求体中的 JSON 自动反序列化为 Java 对象。
定义实体类:
java
import lombok.Data;
@Data
public class User {
private String name;
private int age;
}
控制器方法:
java
@RestController
@RequestMapping("/user")
public class UserController {
// POST /user/create 请求体: {"name":"Alice","age":20}
@PostMapping("/create")
public String createUser(@RequestBody User user) {
return "created: " + user.getName();
}
}
- 需引入 Jackson 依赖(Spring Boot 默认自带),JSON 字段名与对象属性名自动映射。
- 常用
@Valid配合@RequestBody进行参数校验。 - 可额外接收
BindingResult参数获取校验结果。
测试结果



为什么age变a也可以?而name变b,age变a不行👉【Spring】Jackson 属性映射
表单参数与文件上传
表单提交和文件上传也可用 @RequestParam 接收。
java
@RestController
@RequestMapping("/user")
public class UserController {
// POST 表单:name=zhangsan&age=20 (Content-Type: application/x-www-form-urlencoded)
@PostMapping("/form")
public String handleForm(@RequestParam String name,
@RequestParam int age) {
return "name=" + name + ", age=" + age;
}
// 文件上传:需设置 enctype="multipart/form-data"
@PostMapping("/avatar")
public String uploadAvatar(@RequestParam("file") MultipartFile file) {
return "上传文件:" + file.getOriginalFilename() + ",大小:" + file.getSize();
}
}
- 表单参数和查询参数共用
@RequestParam,Spring 自动识别来源(查询字符串或表单体)。 - 文件上传必须使用
multipart/form-data,用MultipartFile类型接收。
测试结果



数组和集合上传
在实际开发中,经常需要一次性传递多个相同类型的参数,例如批量删除用户 ID、批量查询商品 ID 等。Spring MVC 支持直接通过 @RequestParam 绑定到数组或 List 集合。
传递数组上传
当请求参数名相同时,Spring 会自动将多个同名参数组装成一个数组。
java
@RestController
@RequestMapping("/user")
public class UserController {
// GET /user/batch?ids=1&ids=2&ids=3
@GetMapping("/batch")
public String batchQuery(@RequestParam("ids") int[] ids) {
return "查询的 ID 数组:" + Arrays.toString(ids);
}
}
测试结果 :


关键点:
- 多个同名参数
ids=1&ids=2&ids=3会被自动收集到int[] ids中。 - 也支持
String[]、Integer[]等数组类型。 - 如果参数不存在且
required = true(默认),会报 400 错误;可设置required = false或defaultValue。
集合(List)上传
除了数组,也可以直接绑定到 List 集合,但需要显式指定参数名。
java
@RestController
@RequestMapping("/user")
public class UserController {
// GET /user/list?ids=1&ids=2&ids=3
@GetMapping("/list")
public String batchList(@RequestParam("ids") List<Integer> ids) {
return "查询的 ID 列表:" + ids;
}
}
测试结果 :


关键点:
- 使用
List<Integer>时,Spring 会自动将字符串参数转换为对应的泛型类型。 - 如果泛型是自定义对象(如
List<User>),则无法通过@RequestParam直接绑定,需要使用@RequestBody传递 JSON 数组。
JSON 数组(通过 @RequestBody)上传
如果前端传递的是 JSON 数组(如 [1, 2, 3] 或对象数组),则需要使用 @RequestBody。
java
@RestController
@RequestMapping("/user")
public class UserController {
// POST /user/batch 请求体: [1, 2, 3]
@PostMapping("/batch")
public String batchCreate(@RequestBody List<Integer> ids) {
return "批量创建,ID 列表:" + ids;
}
// POST /user/batch-objects
@PostMapping("/batch-objects")
public String batchCreateUsers(@RequestBody List<User> users) {
return "批量创建用户:" + users.size() + " 个";
}
}
测试结果 :

关键点:
@RequestBody可以直接接收 JSON 数组,并自动反序列化为List<Integer>或List<User>。- 需要确保请求头
Content-Type: application/json。 - 适用于 POST、PUT 等需要传递复杂数据结构的场景。
请求头与 Cookie:@RequestHeader、@CookieValue
有时需要读取特定的请求头或 Cookie 值。
java
@RestController
@RequestMapping("/user")
public class UserController {
// 读取单个请求头
@GetMapping("/header")
public String getHeader(@RequestHeader("User-Agent") String userAgent) {
return "User-Agent: " + userAgent;
}
// 读取 Cookie
@GetMapping("/cookie")
public String getCookie(@CookieValue(value = "JSESSIONID", defaultValue = "") String sessionId) {
return "JSESSIONID: " + sessionId;
}
}
- 同样支持
required和defaultValue属性。
参数解析流程(Mermaid 图)
Controller 参数解析器(ArgumentResolvers) HandlerAdapter HandlerMapping DispatcherServlet 客户端 Controller 参数解析器(ArgumentResolvers) HandlerAdapter HandlerMapping DispatcherServlet 客户端 @PathVariable 从 URL 模板提取 @RequestParam 从 QueryString/表单提取 @RequestBody 读取请求体并反序列化 @RequestHeader/@CookieValue 从请求头/Cookie提取 其他类型按需解析 HTTP 请求 查找处理器 返回 HandlerMethod 调用处理器适配器 遍历方法参数进行解析 解析完成的参数数组 反射调用 Controller 方法(传入参数) 返回结果 ModelAndView 或响应体 HTTP 响应
- 框架通过
HandlerMethodArgumentResolver接口为每种注解提供专用解析器,开发者也可自定义解析逻辑。 - 常用参数类型(如
HttpServletRequest、HttpServletResponse、Principal等)同样由此机制注入。
响应返回
返回普通字符串
java
@RequestMapping("/hello")
public String sayHello() {
return "Hello Spring Web";
}
返回 JSON 对象
java
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/info")
public User getUserInfo() {
User user = new User();
user.setName("zhangsan");
user.setAge(25);
return user; // Spring 自动转为 JSON
}
}
@RestController另一个强大之处在于 自动序列化 。当方法返回的是一个对象(例如User实体),Spring 会调用配置好的HttpMessageConverter(默认使用 Jackson 库)将其转换为 JSON 字符串,并设置Content-Type: application/json。
测试结果 :

设置响应状态码
在 Spring MVC 中,设置响应状态码主要有两种方式:使用 @ResponseStatus 注解,或通过 HttpServletResponse 对象手动设置。下面通过对比来理解它们的区别。
方式一:使用 @ResponseStatus 注解
直接在方法上声明,Spring 会在方法执行完成后自动设置状态码。
java
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/delete")
@ResponseStatus(HttpStatus.NO_CONTENT) // 204
public void deleteUser() {
// 删除逻辑,不返回内容
}
}
特点:
- 声明式,代码简洁,一目了然。
- 状态码在编译期就确定,适合固定状态码的场景(如 204、201)。
测试结果

方式二:通过 HttpServletResponse 手动设置
在方法参数中注入 HttpServletResponse,调用 setStatus() 动态设置。
java
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/setstatus")
public void setStatus(HttpServletResponse response) {
response.setStatus(404);
}
}
特点:
- 编程式,可在方法内部根据业务逻辑动态决定状态码。
- 适合需要根据条件返回不同状态码的场景(如 200 成功、404 未找到、500 服务器错误)。
测试结果

两种方式对比
| 对比维度 | @ResponseStatus 注解 | HttpServletResponse.setStatus() |
|---|---|---|
| 设置方式 | 声明式(注解) | 编程式(代码) |
| 状态码确定性 | 编译期固定 | 运行时动态决定 |
| 代码简洁性 | 更简洁 | 稍复杂 |
| 典型场景 | 固定状态码(204、201、404) | 条件分支状态码(根据业务逻辑) |
| 异常处理配合 | 可配合自定义异常使用 | 通常用于正常流程 |
- 两种方式可以混合使用,但注意
@ResponseStatus的优先级低于HttpServletResponse.setStatus(),如果两者同时存在,以setStatus()为准。
返回HTML页面:@Controller
在 Spring MVC 中,返回 HTML 页面与返回 JSON/字符串的关键区别在于控制器类使用的注解。@Controller 用于返回视图(如 JSP、HTML 页面),而 @RestController 则直接返回数据。下面通过一个完整的示例来演示。
准备工作
首先在 src/main/resources/static 目录下创建一个简单的 HTML 页面 index.html:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1>欢迎来到 Spring MVC</h1>
<p>这是一个返回的 HTML 页面</p>
</body>
</html>
使用 @Controller 返回页面
java
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/resp")
public class RespController {
/**
* 返回 HTML 页面
* 方法返回字符串,Spring 会将其解析为视图路径
*/
@RequestMapping("/r1")
public String returnPage() {
return "/aa/index.html";
}
}
关键点:
- 类上使用
@Controller,表示该类中的方法默认返回视图。 - 方法返回的字符串
/index.html会被 Spring MVC 的视图解析器解析为静态资源路径。 - 访问
http://localhost:8080/resp/r1会渲染index.html页面。
测试结果 :

@Controller + @ResponseBody 返回数据
如果希望在 @Controller 类中的某个方法直接返回数据(而非视图),可以在该方法上额外添加 @ResponseBody 注解。
java
@Controller
@RequestMapping("/resp")
public class RespController {
/**
* 返回纯文本数据
*/
@ResponseBody
@RequestMapping("/r2")
public String returnData() {
return "我是前端需要的数据";
}
/**
* 返回 HTML 片段(作为字符串数据返回)
*/
@ResponseBody
@RequestMapping("/r3")
public String returnHTML() {
return "<h1>我是一级标题</h1>";
}
/**
* 返回纯文本(设置 Content-Type 为 text/plain)
*/
@ResponseBody
@RequestMapping(value = "/r4", produces = "text/plain")
public String returnText() {
return "<h1>我是一级标题</h1>";
}
}
测试结果:
/r2 返回纯文本:

/r3 返回 HTML 片段(默认 Content-Type 为 text/html):

/r4 返回纯文本(Content-Type 为 text/plain,浏览器不会渲染 HTML 标签):

@Controller 与 @RestController 的区别
| 对比维度 | @Controller | @RestController |
|---|---|---|
| 默认行为 | 返回视图(JSP/HTML) | 返回数据(JSON/字符串) |
| 是否需要 @ResponseBody | 需要手动添加 | 不需要,默认所有方法都带 @ResponseBody |
| 典型场景 | 返回 HTML 页面 | RESTful API、前后端分离 |
| 组合关系 | - | @RestController = @Controller + @ResponseBody |
- 返回页面 :使用
@Controller,方法返回视图路径字符串。 - 返回数据 :使用
@RestController,或在@Controller方法上添加@ResponseBody。 - 返回 HTML 片段 :使用
@ResponseBody返回包含 HTML 标签的字符串,默认 Content-Type 为text/html。 - 返回纯文本 :通过
produces = "text/plain"指定 Content-Type,浏览器不会渲染 HTML 标签。
设置Content-Type
在 Spring MVC 中,Content-Type 响应头决定了浏览器如何解析返回的数据。默认情况下,@ResponseBody 会根据返回值类型自动设置 Content-Type,但有时我们需要手动指定,例如返回纯文本、XML 或自定义格式。
通过 produces 属性设置
@RequestMapping 的 produces 属性可以指定方法返回数据的 Content-Type,Spring 会据此设置响应头。
java
@Controller
@RequestMapping("/resp")
public class RespController {
/**
* 返回 HTML 片段(作为字符串数据返回)
*/
@ResponseBody
@RequestMapping("/r3")
public String returnHTML() {
return "<h1>我是一级标题</h1>";
}
/**
* 返回纯文本(设置 Content-Type 为 text/plain)
*/
@ResponseBody
@RequestMapping(value = "/r4", produces = "text/plain")
public String returnText() {
return "<h1>我是一级标题</h1>";
}
/**
* 指定 Content-Type 为 application/json
* 返回 JSON 格式数据
*/
@ResponseBody
@RequestMapping(value = "/r5", produces = "application/json")
public User returnJson() {
User userInfo = new User("zhangsan", 20);
return userInfo;
}
/**
* 指定 Content-Type 为 application/xml
* 返回 XML 格式数据(需引入 Jackson XML 扩展)
*/
@ResponseBody
@RequestMapping(value = "/r6", produces = "application/xml")
public User returnXml() {
User userInfo = new User("lisa", 21);
return userInfo;
}
}
测试结果:
/r3 默认 text/html,浏览器渲染 HTML 标签:

/r4 指定 text/plain,浏览器直接显示源码:

/r5 指定 application/json,浏览器显示json:

通过 HttpServletResponse 手动设置
也可以在方法内部通过 HttpServletResponse.setContentType() 动态设置。
java
@Controller
@RequestMapping("/resp")
public class RespController {
@ResponseBody
@RequestMapping("/r7")
public String setContentType(HttpServletResponse response) {
// 手动设置 Content-Type
response.setContentType("text/plain;charset=UTF-8");
return "<h1>我是一级标题</h1>";
}
}
测试结果 :

常见 Content-Type 取值
两种方式对比
| 对比维度 | produces 属性 | HttpServletResponse.setContentType() |
|---|---|---|
| 设置方式 | 声明式(注解) | 编程式(代码) |
| 灵活性 | 编译期固定 | 运行时动态决定 |
| 代码简洁性 | 更简洁 | 稍复杂 |
| 典型场景 | 固定 Content-Type | 根据业务逻辑动态设置 |
- 两种方式可以混合使用,但
HttpServletResponse.setContentType()的优先级更高。 - 设置
Content-Type时建议同时指定字符编码(如text/plain;charset=UTF-8),避免中文乱码。
设置Header
在实际开发中,有时需要在响应中添加自定义 Header,例如设置分页信息、认证令牌、缓存控制等。Spring MVC 提供了多种方式来设置响应 Header。
方式一:通过 HttpServletResponse 手动设置
在方法参数中注入 HttpServletResponse,调用 setHeader() 或 addHeader() 方法。
java
@RestController
@RequestMapping("/resp")
public class RespController {
@ResponseBody
@RequestMapping("/r8")
public String setHeader(HttpServletResponse response) {
// 设置单个 Header
response.setHeader("myHeader", "myHeaderValue");
// 设置多个 Header
response.setHeader("X-Custom-Header", "custom-value");
// addHeader 与 setHeader 的区别:
// setHeader:如果同名 Header 已存在,会覆盖
// addHeader:如果同名 Header 已存在,会追加(允许重复)
response.addHeader("X-Trace-Id", "trace-123456");
return "设置Header成功";
}
}
测试结果 :

关键点:
| 方法 | 说明 | 示例 |
|---|---|---|
setHeader(String name, String value) |
设置单个 Header,同名时覆盖 | response.setHeader("myHeader", "myValue") |
addHeader(String name, String value) |
追加 Header,同名时不会覆盖,响应中会出现多个同名 Header | response.addHeader("X-Trace-Id", "trace-123") |
setIntHeader(String name, int value) |
设置整数值的 Header(如 Content-Length) |
response.setIntHeader("Content-Length", 1024) |
setDateHeader(String name, long date) |
设置日期类型的 Header(如 Expires) |
response.setDateHeader("Expires", System.currentTimeMillis() + 86400000) |
方式二:通过 @ResponseHeader 注解(Spring 6.0+)
从 Spring 6.0 开始,可以使用 @ResponseHeader 注解在方法上声明式地设置响应 Header。
java
import org.springframework.web.bind.annotation.ResponseHeader;
@RestController
@RequestMapping("/resp")
public class RespController {
@RequestMapping("/r9")
@ResponseHeader(name = "X-Custom-Header", value = "annotation-value")
public String setHeaderByAnnotation() {
return "通过注解设置Header成功";
}
}
⚠️ 注意:
@ResponseHeader是 Spring 6.0 / Spring Boot 3.0 新增的注解,如果使用旧版本,请使用HttpServletResponse方式。
方式三:通过 ResponseEntity 设置
ResponseEntity 允许同时设置状态码、Header 和响应体,是最灵活的方式。
java
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@RestController
@RequestMapping("/resp")
public class RespController {
@RequestMapping("/r10")
public ResponseEntity<String> setHeaderByResponseEntity() {
HttpHeaders headers = new HttpHeaders();
headers.add("X-Pagination-Page", "1");
headers.add("X-Pagination-Total", "100");
headers.add("X-Custom-Header", "response-entity-value");
return new ResponseEntity<>("设置Header成功(ResponseEntity)", headers, HttpStatus.OK);
}
}
测试结果 :

关键点:
ResponseEntity可以同时控制状态码、Header 和响应体。HttpHeaders支持链式调用:headers.add("key", "value")。- 适合需要动态设置多个 Header 的场景。
常见 Header 设置场景
| Header 名称 | 说明 | 示例值 |
|---|---|---|
X-Trace-Id |
请求追踪 ID,用于日志链路 | trace-123456 |
X-Pagination-Page |
分页页码 | 1 |
X-Pagination-Total |
分页总数 | 100 |
X-RateLimit-Remaining |
接口限流剩余次数 | 50 |
Cache-Control |
缓存控制 | no-cache, no-store, must-revalidate |
Expires |
过期时间 | 0 |
Content-Disposition |
文件下载时的文件名 | attachment; filename="report.pdf" |
三种方式对比
| 对比维度 | HttpServletResponse | @ResponseHeader 注解 | ResponseEntity |
|---|---|---|---|
| 设置方式 | 编程式(代码) | 声明式(注解) | 编程式(对象) |
| 灵活性 | 高,可动态设置 | 低,编译期固定 | 高,可动态设置 |
| 代码简洁性 | 中等 | 最简洁 | 稍复杂 |
| 批量设置 | 需多次调用 | 需多个注解 | 支持链式添加 |
| 同时设置状态码 | 需额外调用 setStatus | 需配合 @ResponseStatus | 直接支持 |
| 典型场景 | 动态 Header、条件分支 | 固定 Header | 需要同时控制状态码和 Header |
- 三种方式可以混合使用,但
HttpServletResponse的优先级最高。 - 推荐:简单固定 Header 用
@ResponseHeader(Spring 6.0+),复杂场景用ResponseEntity,需要极致灵活性用HttpServletResponse。