@Controller 与 @RestController 注解详解与区别(Spring Boot 面试必背)
前言
在 Spring Boot / SpringMVC 后端开发中,@Controller 与 @RestController 是最基础且高频使用的控制器注解,也是初级后端面试中必考的知识点。很多初学者容易混淆两者的用法,常常出现“页面跳转失败”“接口返回JSON报错”“404异常”等问题。
本文将从「注解本质、核心作用、跳转逻辑、代码示例、源码解析、核心区别、坑点避坑、面试问答」8个维度,全方位拆解两个注解,重点讲清大家最易困惑的“页面跳转逻辑”,帮你彻底分清用法、吃透考点,看完直接能用于开发和面试。
一、@Controller 注解详解
1. 基本定义
@Controller 是 SpringMVC 原生注解(Spring 2.5+ 引入),核心作用是 标记一个类为 Web 控制器,将该类交由 Spring 容器管理。它负责接收前端发送的 HTTP 请求、调用业务逻辑层处理请求,默认行为是返回视图名称,实现页面跳转,是传统前后端不分离项目的核心控制器注解。
2. 核心特性
- 标识性:被 @Controller 标记的类,会被 Spring 扫描并实例化为控制器 Bean,成为 SpringMVC 处理请求的核心组件。
- 默认返回值:方法返回值默认为「视图名」,SpringMVC 会通过视图解析器(如 Thymeleaf、JSP 解析器)拼接路径,最终跳转到对应页面。
- JSON 返回方式:若需要返回 JSON/XML 数据(而非跳转页面),必须在对应方法上手动添加 @ResponseBody 注解,否则 Spring 会将返回值当作视图名解析,导致报错。
- 适用场景:传统前后端不分离项目(如 JSP、Thymeleaf 模板渲染的后台管理系统),需要实现页面跳转、表单提交与重定向的场景。
3. 代码示例
下面以最常见的“跳转首页”为例,逐行解释代码作用,重点讲清「谁负责匹配请求、谁负责跳转页面」:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
// 标记当前类为SpringMVC控制器,交由Spring容器管理
@Controller
// 类级别的请求映射:所有该类的接口都需加上 "/page" 前缀
@RequestMapping("/page")
public class PageController {
// 方法级别的请求映射:匹配前端访问路径 "/page/index"
@GetMapping("/index")
public String toIndex() {
// 返回视图名 "index",这是实现页面跳转的核心
return "index";
}
}关键疑问解答
页面跳转是 @GetMapping("/index") 的结果,还是 return "index"; 的结果?
答案:@GetMapping("/index") 负责匹配请求,return "index"; 负责真正跳转页面,两者分工明确,缺一不可,完整流程如下:
- 前端发起请求:浏览器访问 http://localhost:8080/page/index(假设项目端口为8080);
- 请求匹配:SpringMVC 先通过类上的 @RequestMapping("/page"),再通过方法上的 @GetMapping("/index"),拼接出完整路径 /page/index,找到 toIndex() 方法并执行;
- 视图名返回:方法执行完毕后,return "index" 会将字符串 "index" 作为「视图名」返回给 SpringMVC;
- 页面定位与跳转:SpringMVC 调用视图解析器,拼接路径(以 Thymeleaf 为例,默认配置为 classpath:/templates/ + 视图名 + .html),即 classpath:/templates/index.html,最终找到对应页面并渲染,完成跳转。
补充:若视图解析器配置的前缀是 /WEB-INF/jsp/、后缀是 .jsp,则 return "index" 会定位到 /WEB-INF/jsp/index.jsp页面。
4. @Controller 返回 JSON 数据的示例
如果用 @Controller 想返回 JSON 数据,必须在方法上添加 @ResponseBody 注解,否则会报错(Spring 会把返回的 Map 当作视图名解析,找不到对应页面):
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
import java.util.Map;
@Controller
@RequestMapping("/user")
public class UserController {
// 手动添加 @ResponseBody,指定该方法返回 JSON 数据
@GetMapping("/info")
@ResponseBody
public Map<String, Object> getUserInfo() {
Map<String, Object> user = new HashMap<>();
user.put("id", 1001);
user.put("name", "张三");
user.put("age", 25);
// 返回的 Map 会被 Spring 自动序列化为 JSON 写入响应体
return user;
}
}二、@RestController 注解详解
1. 基本定义
@RestController 是 Spring 4.0 新增的组合注解,核心作用是 用于前后端分离项目的 RESTful API 开发,它等价于「@Controller + @ResponseBody」的组合,无需手动给方法添加 @ResponseBody,默认所有方法都返回 JSON/XML 数据,不跳转页面。
2. 核心特性
- 组合性:源码中明确标注了 @Controller 和 @ResponseBody 两个注解,本质是两者的“简化写法”,减少代码冗余。
- 默认返回值:所有方法默认自带 @ResponseBody 效果,返回值会被 Spring 自动序列化为 JSON/XML 数据,写入 HTTP 响应体,不会经过视图解析器,无法跳转页面。
- 适用场景:前后端分离项目(如 Vue、React、小程序对接后端接口),仅需要返回数据、不需要页面跳转的场景,是开发 RESTful API 的首选注解。
- Spring 版本要求:仅支持 Spring 4.0 及以上版本(Spring Boot 1.0+ 已默认集成,可直接使用)。
3. 源码解析
打开 @RestController 的源码,就能清晰看到它的本质,相当于“一键添加两个注解”:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller // 继承 @Controller 的控制器功能
@ResponseBody // 继承 @ResponseBody 的 JSON 序列化功能
public @interface RestController {
String value() default ""; // 可指定控制器的Bean名称,默认空
}4. 代码示例(RESTful API 开发)
无需手动添加 @ResponseBody,方法返回值直接转为 JSON,适配前后端分离接口调用:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
// 组合注解:等价于 @Controller + @ResponseBody
@RestController
@RequestMapping("/api/user")
public class ApiUserController {
// 无需加 @ResponseBody,默认返回 JSON
@GetMapping("/{id}")
public Map<String, Object> getUserById(@PathVariable Integer id) {
Map<String, Object> user = new HashMap<>();
user.put("id", id);
user.put("name", "李四");
user.put("gender", "男");
user.put("address", "北京市");
return user;
}
// 所有方法都默认返回 JSON
@GetMapping("/list")
public Map<String, Object> getUserList() {
Map<String, Object> result = new HashMap<>();
result.put("code", 200);
result.put("msg", "success");
result.put("data", new String[]{"张三", "李四", "王五"});
return result;
}
}三、@Controller 与 @RestController 核心区别
为了方便记忆和面试作答,整理了清晰的对比表格,覆盖所有核心维度:
| 对比维度 | @Controller | @RestController |
|---|---|---|
| 注解本质 | SpringMVC 原生控制器注解 | 组合注解(@Controller + @ResponseBody) |
| 默认返回值 | 视图名(用于页面跳转) | JSON/XML 数据(用于接口响应) |
| @ResponseBody 需求 | 返回 JSON 需手动添加(方法级) | 默认自带(类级生效,无需添加) |
| 视图解析 | 会经过视图解析器,可跳转页面 | 不经过视图解析器,无法跳转页面 |
| Spring 版本 | Spring 2.5+ | Spring 4.0+ |
| 适用场景 | 传统前后端不分离(页面跳转) | 前后端分离(RESTful API 开发) |
| 常见异常 | 未加 @ResponseBody 返回 404(视图找不到) | 试图跳转页面失败(返回字符串被当作 JSON) |
一句话总结:@Controller 用于页面跳转,返回 JSON 需加 @ResponseBody;@RestController 用于接口开发,默认所有方法返回 JSON,无法跳转页面。
四、常见坑点与避坑指南
坑点 1:@RestController 试图跳转页面,报 404
错误场景:用 @RestController 标记控制器,方法 return "index",想跳转 index.html 页面,结果报 404。
原因:@RestController 自带 @ResponseBody,return "index" 会被当作 JSON 字符串返回,不会经过视图解析器,自然找不到页面。
解决方案:页面跳转必须用 @Controller 标记控制器,不要用 @RestController。
坑点 2:@Controller 未加 @ResponseBody,返回 JSON 报 404
错误场景:@Controller 标记的方法,返回 Map/实体类(想返回 JSON),未加 @ResponseBody,报 404。
原因:Spring 会将返回的 Map/实体类当作“视图名”解析,找不到对应视图,所以报 404。
解决方案:在返回 JSON 的方法上,手动添加 @ResponseBody 注解。
坑点 3:同一个控制器混用页面跳转和接口返回
错误场景:一个控制器用 @Controller 标记,既有跳转页面的方法,也有返回 JSON 的方法,但部分返回 JSON 的方法忘记加 @ResponseBody。
解决方案:拆分控制器,专人专责——页面跳转控制器用 @Controller,API 接口控制器用 @RestController;若必须混用,确保所有返回 JSON 的方法都加 @ResponseBody。
五、使用场景选择
- 传统 Web 项目(JSP/Thymeleaf 模板):需要页面跳转、表单提交、重定向 → 用 @Controller。
- 前后端分离项目(Vue/React/小程序):仅提供接口、返回数据,不涉及页面渲染 → 用 @RestController。
- 混合场景(极少用):同一个项目既有页面跳转,又有少量接口 → 用 @Controller + 接口方法加 @ResponseBody。
六、面试高频问答
Q1:@RestController 的作用是什么?
A:@RestController 是 Spring 4.0 新增的组合注解,等价于 @Controller + @ResponseBody,用于 RESTful API 开发;它会将控制器交由 Spring 容器管理,且所有方法默认返回 JSON/XML 数据,无需手动添加 @ResponseBody,无法跳转页面。
Q2:@Controller 如何返回 JSON 数据?
A:在 @Controller 标记的方法上,手动添加 @ResponseBody 注解,Spring 会自动将方法的返回值(Map、实体类等)序列化为 JSON 数据,写入 HTTP 响应体。
Q3:@Controller 和 @RestController 的最大区别是什么?
A:最大区别是「是否默认自带 @ResponseBody」:@Controller 默认返回视图名,用于页面跳转;@RestController 默认自带 @ResponseBody,返回 JSON 数据,用于接口开发,无法跳转页面。
Q4:用 @RestController 能实现页面跳转吗?怎么实现?
A:不能直接实现。因为 @RestController 自带 @ResponseBody,返回值会被当作 JSON 解析;若非要用它跳转页面,需手动在方法上添加 @ResponseBody(抵消类级注解),且返回 ModelAndView 对象,但这种用法不推荐,建议直接用 @Controller。
七、总结
1. 核心区分:@Controller 管“页面跳转”,@RestController 管“接口数据”,两者本质是“是否默认包含 @ResponseBody”。
2. 开发建议:前后端分离优先用 @RestController,传统项目用 @Controller,避免混用导致异常。
3. 面试重点:记住“@RestController = @Controller + @ResponseBody”,以及两者的返回值差异、适用场景,就能应对绝大多数相关面试题。
4. 避坑关键:不要用 @RestController 跳转页面,不要忘记给 @Controller 的 JSON 方法加 @ResponseBody。