@Controller 与 @RestController:返回值与视图解析
Spring MVC 里处理请求的类叫控制器(Controller)。同样是返回一个字符串,可能是「视图名」也可能是「JSON 文本」,区别全在注解与返回值类型上。这一章把 @Controller、@RestController 和各种返回值捋清楚。
@Controller:传统的 MVC 控制器
@Controller 是 @Component 的衍生注解,会被组件扫描发现。方法返回逻辑视图名,由 ViewResolver 解析成视图渲染 HTML:
@Controller
public class HelloController {
@GetMapping("/hello")
public String hello(Model model) {
model.addAttribute("msg", "Hello Spring MVC");
return "hello"; // 逻辑视图名,交给 ViewResolver
}
}
视图解析:ViewResolver 的配合
ViewResolver 负责把逻辑视图名变成真实视图。典型配置是前缀 + 后缀(JSP),或用 Thymeleaf 的模板解析器:
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/jsp/"); // hello → /WEB-INF/jsp/hello.jsp
resolver.setSuffix(".jsp");
return resolver;
}
}
// 说明:Thymeleaf 场景需引入 thymeleaf-spring6,用 SpringTemplateEngine + ThymeleafViewResolver,
// 版本匹配与装配方式以官方文档为准
多个 ViewResolver 可以共存并按顺序尝试,找不到对应视图时继续交给下一个。
@RestController:直接写数据
@RestController = @Controller + @ResponseBody(类级别)。方法返回值不再当视图名,而是经 HttpMessageConverter 序列化后直接写入响应体(默认 JSON):
@RestController
public class ApiController {
@GetMapping("/api/users/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id); // 序列化成 JSON 返回
}
}
// 响应示例:{"id":1,"name":"张三"}
常用返回值类型速查
| 返回值类型 | 行为 |
|---|---|
| String(无 @ResponseBody) | 当作逻辑视图名,走 ViewResolver |
| String(有 @ResponseBody) | 直接作为响应体文本 |
| Model / ModelMap / Map | 只往模型加数据,视图名由 RequestToViewNameTranslator 推断 |
| ModelAndView | 模型 + 视图一起返回 |
| @ModelAttribute 对象(无 @ResponseBody) | 先加入模型,再按视图渲染 |
| 任意对象 + @ResponseBody | 交给 HttpMessageConverter 写出 |
| void | 靠 HttpServletResponse 自行写响应,或仅靠 @ResponseStatus 表示状态 |
| redirect:/xxx | 重定向;forward:/xxx 为服务器内部转发 |
两个典型踩坑点
- 在 @Controller 中方法直接返回字符串 "OK",会被当成视图名去解析,于是报 404 或视图解析失败;想返回文本必须加 @ResponseBody,或干脆用 @RestController。
- 返回对象序列化 JSON 需要类上有 Jackson 库(MappingJackson2HttpMessageConverter 默认在类路径包含 Jackson 时注册),字段没有 getter 或没有无参构造都可能序列化失败。
一句话记忆:想要「页面」用 @Controller 返回视图名,想要「数据」用 @RestController 返回对象。返回值到底是视图还是响应体,取决于有没有 @ResponseBody 以及返回值类型,别让 Spring 猜。