Spring 控制器(@Controller、@RestController)

Spring 控制器 @Controller 与 @RestController 全解析(2025 版)

恭喜你继续深挖 Spring MVC!前面讲了 DispatcherServlet,这次专攻控制器(Controller),让你彻底搞懂 @Controller 和 @RestController 的区别、使用场景、源码原理、常见坑,以及 2025 年最新最佳实践。内容实用、可直接复制代码到项目。

一、核心区别一览表(面试必背)

项目@Controller@RestController
注解组成仅 @Component@Controller + @ResponseBody
方法返回值默认处理String → 视图名(跳转页面)任意对象 → 直接转 JSON 返回
适用场景返回 HTML 页面(Thymeleaf、JSP)返回 JSON(前后端分离、移动端 API)
是否需要 @ResponseBody每个方法都需要手动加全类自动生效,无需加
Spring Boot 默认推荐传统网页项目99% 的现代项目(API 接口)
常见搭配@RequestMapping + Model@GetMapping + POJO/DTO

结论:如果你是写 API 接口,直接用 @RestController,一劳永逸。
只有需要返回网页模板时才用 @Controller。

二、基本使用示例(Spring Boot 3.x,直接复制运行)

// 1. @RestController 示例(推荐,99% 项目用这个)
@RestController
@RequestMapping("/api/users")
public class UserApiController {

    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        // 直接返回对象,自动转 JSON: {"id":1,"name":"张三"}
        return new User(id, "张三");
    }

    @PostMapping
    public User create(@RequestBody User user) {
        // 自动把 JSON 转成 User 对象
        return userService.save(user);
    }

    @GetMapping
    public List<User> list() {
        return userService.list();
    }
}

// 2. @Controller 示例(返回网页)
@Controller
@RequestMapping("/page")
public class PageController {

    @GetMapping("/user/{id}")
    public String getUser(@PathVariable Long id, Model model) {
        model.addAttribute("user", userService.get(id));
        // 返回视图名,自动去找 /templates/user/detail.html
        return "user/detail";
    }

    // 如果某个方法想返回 JSON,必须手动加 @ResponseBody
    @GetMapping("/user/json/{id}")
    @ResponseBody
    public User getUserJson(@PathVariable Long id) {
        return userService.get(id);
    }
}

三、@RestController 内部到底是怎么实现的?(源码解析)

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody   // ← 关键就在这里!
public @interface RestController {
    // ...
}
  • DispatcherServlet 调用你的方法后
  • 看到类上有 @ResponseBody → 使用 HttpMessageConverter(默认 Jackson)把返回值转成 JSON 并写入 response body
  • 没有 @ResponseBody → 当成视图名,去 ViewResolver 找模板文件

四、常用请求映射注解(Spring Boot 提供,超级好用)

@RestController
@RequestMapping("/api/books")
public class BookController {

    @GetMapping("/{id}")          // GET    /api/books/1
    public Book get(@PathVariable Long id) { ... }

    @PostMapping                  // POST   /api/books
    public Book create(@RequestBody Book book) { ... }

    @PutMapping("/{id}")          // PUT    /api/books/1
    public Book update(@PathVariable Long id, @RequestBody Book book) { ... }

    @DeleteMapping("/{id}")       // DELETE /api/books/1
    public void delete(@PathVariable Long id) { ... }

    @PatchMapping("/{id}")        // PATCH  /api/books/1
    public Book patch(...) { ... }
}

五、方法参数与返回值高频用法(30+ 种参数解析器)

@GetMapping("/search")
public List<User> search(
    @RequestParam String name,                   // ?name=张三
    @RequestParam(required = false) Integer age, // 可选参数
    @RequestParam(defaultValue = "1") int page,  // 默认值
    HttpServletRequest request,                  // 原生对象
    @CookieValue("token") String token,          // Cookie
    @RequestHeader("User-Agent") String ua) {    // Header
    ...
}

返回值常见类型(@RestController):

  • POJO → JSON 对象
  • List → JSON 数组
  • Map → JSON 对象
  • String → 纯文本(很少用)
  • ResponseEntity → 可自定义状态码和头
  • void → 状态 200(常配合 @ResponseStatus)

六、生产级最佳实践(2025 年推荐)

  1. 统一响应格式(所有接口返回一致结构)
// 推荐返回类
public class R<T> {
    private int code;
    private String msg;
    private T data;
    public static <T> R<T> ok(T data) { ... }
    public static <T> R<T> error(String msg) { ... }
}

// 使用
@GetMapping("/{id}")
public R<User> get(@PathVariable Long id) {
    User user = userService.get(id);
    return R.ok(user);
}
  1. 全局异常处理(别让异常直接返回 500)
@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(Exception.class)
    public R<String> handle(Exception e) {
        return R.error("服务器异常:" + e.getMessage());
    }
}
  1. 参数校验(JSR-303)
@PostMapping
public R<User> create(@Valid @RequestBody User user) { ... }

// User 类
public class User {
    @NotBlank(message = "姓名不能为空")
    private String name;

    @Email(message = "邮箱格式错误")
    private String email;
}
  1. 跨域处理(前后端分离必备)
// 全局跨域
@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowedMethods("*")
                .allowedHeaders("*");
    }
}

七、常见坑 & 避坑指南

  • 坑1:用了 @Controller 但忘记加 @ResponseBody → 返回视图名,导致 404
    解决:改成 @RestController
  • 坑2:@RequestMapping 在类和方法上重复写路径
    推荐:类上写共同前缀,方法上只写剩余部分(如上例)
  • 坑3:返回值是 null → JSON 返回 null 或空对象
    解决:Jackson 配置 @JsonInclude(Include.NON_NULL)
  • 坑4:大文件上传报错
    解决:在 application.yml 配置
  spring:
    servlet:
      multipart:
        max-file-size: 100MB
        max-request-size: 100MB

八、总结推荐(2025 年)

项目场景推荐注解理由
前后端分离 API@RestController直接返回 JSON,简洁
传统网页项目@Controller返回 Thymeleaf/JSP 页面
混合项目(既有页面又有API)两个都用,分开写 Controller清晰分离

一句话口诀
写接口用 @RestController,返回页面用 @Controller。
99% 的新项目直接全用 @RestController + 统一响应体 R 就够了!

你现在可以直接复制上面的代码创建一个完整的 CRUD 接口项目跑起来!

下一步想学什么?

  • 统一响应体 + 全局异常处理(完整代码)
  • 参数校验 @Valid + 自定义校验器
  • 分页查询(PageHelper 或 MyBatis-Plus)
  • Swagger/OpenAPI 文档自动生成
  • 控制器单元测试(@WebMvcTest)
    直接告诉我,我继续给你超详细的实战教程!
文章已创建 3070

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部