@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 猜。

笔记加载中…