在 Spring MVC 开发中,控制器如何响应客户端请求是每个开发者必须掌握的核心技能。无论你是刚接触 Java 后端,还是正在转向微服务架构,理解响应机制都能让你事半功倍。本文将从静态页面返回、注解区别、数据定制三个维度,深入剖析 Spring MVC 的响应处理,并穿插与其他语言(如 Go、C++、Python、TypeScript)的对比,帮助你建立完整的知识体系。
1. 返回静态页面:从基础到细节
首先,我们从一个最简单的场景开始:返回一个静态 HTML 页面。假设你有一个自定义的 index.html 文件,放置在资源目录下:

后端代码通常如下所示,使用 @RestController 注解的控制器直接返回字符串:
@RequestMapping("/page")
@RestController
public class PageController {
// 返回一个静态的 html 页面
@RequestMapping("/p1")
public String p1(){
return "index.html";
}
}
此时,浏览器会显示页面的原始内容,因为响应头中的 Content-Type 是 text/plain,浏览器将其当作纯文本渲染。那么,如何让浏览器真正解析 HTML?有两种修改方式:
修改返回的数据
第一种方式是直接修改返回的字符串内容,例如拼接 HTML 标签:
@RequestMapping("/page")
@RestController
public class PageController {
// 返回一个静态的 html 页面
@RequestMapping("/p1")
public String p1(){
return "/index.html";
}
}
这样虽然能显示简单格式,但依然不是真正的页面跳转。更好的方式是调整注解和路径。
修改注解与路径
将 @RestController 改为 @Controller,并将返回的字符串从 "index.html" 改为 "/index.html":
@RequestMapping("/page")
//@RestController
@Controller
public class PageController {
// 返回一个静态的 html 页面
@RequestMapping("/p1")
public String p1(){
return "index.html";
}
}
@RequestMapping("/page")
//@RestController
@Controller
public class PageController {
// 返回一个静态的 html 页面
@RequestMapping("/p1")
public String p1(){
return "/index.html";
}
}
此时,Spring 会将字符串解析为视图名称,并查找对应的静态资源,最终返回真正的 HTML 页面。这里有一个实践建议:前后端路径统一加上前导斜杠,避免歧义。不过,在前后端分离的架构中,后端很少直接返回页面,这个功能更多用于单体应用或简单原型。
小贴士:如果你使用过 Python 的 Flask 或 Go 的 net/http,会发现它们也有类似的路由与模板引擎机制,但 Spring MVC 的视图解析器更为强大,支持多种模板引擎(如 Thymeleaf、FreeMarker)。
2. 深入理解 @RestController、@Controller 与 @ResponseBody
很多初学者对这三个注解感到困惑,其实它们的关系非常清晰。我们先看 Spring 的默认扫描路径:启动类所在目录及其子目录,只有被扫描到的类才会被注册为 Bean。

核心公式:@RestController = @Controller + @ResponseBody。下面逐一拆解:
- @Controller(
@Controller):标记类为 MVC 控制器,通常用于返回视图名称。它只能用于类,不能用于方法。 - @ResponseBody(
@ResponseBody):将方法的返回值直接写入 HTTP 响应体,而不是解析为视图。可用于类或方法。 - @RestController(
@RestController):组合注解,类中所有方法默认返回 JSON/XML 数据,而不是视图。

⚠️ 特别注意:@ResponseBody 不能单独使用,必须配合 @Controller 才有意义。如果你用过 TypeScript 的 Express 框架,可以类比为 res.json() 与 res.render() 的区别。
实际用法总结
- 一个类中既需要返回页面又需要返回数据:类上使用
@Controller,方法上按需添加@ResponseBody。 - 一个类全部返回数据:直接使用
@RestController,或@Controller+ 所有方法加@ResponseBody。 - 一个类全部返回视图:类上使用
@Controller,但在前后端分离的今天,这种做法已不常见。
延伸思考:在 C++ 的 Web 框架(如 Drogon)中,路由处理函数的返回值通常直接作为响应体,这与 @RestController 的行为类似,但 Spring 的注解驱动模型更灵活。
3. 定制响应:HTML 片段、Content-Type 与状态码
除了返回完整页面,我们经常需要返回 HTML 片段,或者自定义响应头。Spring MVC 提供了丰富的 API 来精细控制响应。
返回 HTML 片段
通过代码直接返回一个 HTML 片段,浏览器默认的 Content-Type 是 text/html,因此会正确渲染:
@ResponseBody
@RequestMapping("/p2")
public String p2(){
return "<h1>我是一级标题</h1>";
}

修改 Content-Type
如果希望浏览器将响应当作纯文本,可以手动修改 Content-Type:
@ResponseBody
@RequestMapping(value = "/p3",produces = "text/plain")
public String p3(){
return "<h1>我是一级标题</h1>";
}

此时浏览器会显示纯文本内容,而不是渲染 HTML。这在返回 JSON 或 XML 时尤其重要。
返回对象:默认 JSON
当控制器方法返回一个对象时,Spring 会自动将其序列化为 JSON,Content-Type 默认为 application/json:
@ResponseBody
@RequestMapping("/p4")
public User p4(){
User user = new User("张三",18,"男生");
return user;
}

抓包可以看到响应头:

为什么默认是 JSON?因为 JSON 是前后端数据交互的事实标准,方便 JavaScript 直接处理。如果你熟悉 Python 的 FastAPI,也会发现它默认返回 JSON,但 Spring 的序列化机制更依赖 Jackson 库。
设置响应状态码
我们可以通过 @ResponseStatus 注解或编程方式设置状态码:
@ResponseBody
@RequestMapping("/p5")
public User setStatus(HttpServletResponse response){
response.setStatus(404);
return new User("张三",18,"男生");
}
抓包结果:

状态码与页面展示没有直接关系。例如,404 状态码并不代表页面必须显示错误信息,而是由前端根据状态码自定义设计。B 站就有专门的 404 页面:

设置 Header 数据
通过 HttpServletResponse 对象,可以添加自定义响应头:
@ResponseBody
@RequestMapping("/p6")
public String setHeader(HttpServletResponse response){
response.setHeader("username","hhs");
return "设置 Header 成功";
}
抓包结果:

这在实现跨域、缓存控制或携带业务元数据时非常有用。在 Go 的 Gin 框架中,也有类似的 Header() 方法,但 Spring 的注解方式更声明式。
4. 实践建议与常见陷阱
基于以上内容,这里给出几点实践建议:
- 优先使用 @RestController:在前后端分离项目中,几乎不需要返回视图,直接返回 JSON 即可。
- 路径规范:静态资源路径统一加
/,避免因路径解析不一致导致 404。 - 明确 Content-Type:当返回对象时,确保依赖注入 Jackson 依赖,否则会报错。
- 状态码语义:不要滥用 200,应根据业务结果返回合适的 4xx/5xx 状态码。
如果你正在学习 Spring MVC,建议结合一个简单的登录或计算器案例来练习,这样能更快掌握响应处理的精髓。
[AFFILIATE_SLOT_1]5. 总结
本文详细介绍了 Spring MVC 中返回静态页面的两种方式、@RestController / @Controller / @ResponseBody 的区别与用法,以及如何定制响应数据的 Content-Type、状态码和 Header。核心要点是:注解决定返回值语义,API 控制响应细节。掌握这些,你就能灵活应对各种前后端交互场景。
最后,如果你觉得文章有帮助,欢迎点赞评论。下一篇将带来 Spring MVC 综合案例:计算器与登录功能实现,敬请期待!
[AFFILIATE_SLOT_2]
浙公网安备 33010602011771号