文章

Spring MVC 学习笔记:请求处理与参数绑定

牛耕田

暂存笔记,持续补充中。Spring MVC 是 Spring Boot Web 的底层骨架,理解它才能用好 @RestController

一、核心组件与请求链路

Spring MVC 是典型的前端控制器模式,所有请求都先到达 DispatcherServlet,再由它分发给具体组件。

浏览器 → DispatcherServlet → HandlerMapping → HandlerAdapter
       → Handler(Controller) → ViewResolver → View → 响应
组件 职责
DispatcherServlet 前端控制器,统一调度入口
HandlerMapping 根据 URL 找到处理器(映射不等于处理器本身)
HandlerAdapter 适配不同类型的处理器(注解式 / 传统接口式)
HandlerExceptionResolver 统一异常处理
ViewResolver 将逻辑视图名解析为具体 View
MultipartResolver 文件上传解析
LocaleResolver / ThemeResolver 国际化 / 主题

一次请求的完整流程:

  1. DispatcherServlet 收到请求,通过 HandlerMapping 得到 HandlerExecutionChain(含拦截器链)。
  2. 依次执行 preHandle
  3. HandlerAdapter 调用 Handler(即 Controller 方法)。
  4. 返回 ModelAndViewpostHandle 执行。
  5. ViewResolver 解析视图并渲染。
  6. afterCompletion 执行。

RESTful 场景:使用 @ResponseBody(或 @RestController)时,返回的是 HttpMessageConverter 序列化后的 JSON,不经过 ViewResolver

二、请求映射

@RestController
@RequestMapping("/api/users")
public class UserController {

    @GetMapping("/{id}")                       // 路径变量
    public User get(@PathVariable Long id) { ... }

    @PostMapping                               // 提交
    public User create(@RequestBody User user) { ... }

    @PutMapping("/{id}")                       // 全量更新
    public User update(@PathVariable Long id, @RequestBody User user) { ... }

    @DeleteMapping("/{id}")                    // 删除
    public void delete(@PathVariable Long id) { ... }
}
  • 组合注解@GetMapping = @RequestMapping(method = RequestMethod.GET)
  • 路径匹配:支持 *(单层)、**(多层);Spring 6 默认改用 PathPatternParser,性能优于旧的 AntPathMatcher
  • 多条件限定:可用 paramsheadersconsumesproduces 做更细粒度的映射。

三、参数绑定(高频考点)

场景 注解 说明
查询字符串 ?a=1 @RequestParam required=false 可设为可选
路径变量 /user/{id} @PathVariable {id} 占位
请求体 JSON @RequestBody HttpMessageConverter 反序列化
表单对象 无注解(POJO) 按字段名自动绑定
请求头 @RequestHeader
Cookie @CookieValue
文件 @RequestPart / MultipartFile
@PostMapping("/search")
public PageResult<User> search(
        @RequestParam(defaultValue = "1") int page,
        @RequestParam(defaultValue = "10") int size,
        @RequestParam(required = false) String keyword,
        @RequestHeader("Authorization") String token) {
    ...
}

常见坑application/json 请求体只能用 @RequestBody,用 @RequestParam 收不到;反之表单提交也不能用 @RequestBody

四、返回值处理

  • @ResponseBody / @RestController:返回值经 HttpMessageConverter 转 JSON。
  • 返回 ResponseEntity<T>:可自定义状态码与响应头。
@GetMapping("/{id}")
public ResponseEntity<User> get(@PathVariable Long id) {
    User u = service.findById(id);
    return u == null ? ResponseEntity.notFound().build()
                     : ResponseEntity.ok(u);
}

五、全局异常处理

@RestControllerAdvice + @ExceptionHandler 统一收口,避免每个方法写 try-catch。

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(BizException.class)
    public Result<Void> handleBiz(BizException e) {
        return Result.fail(e.getCode(), e.getMessage());
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<Void> handleValid(MethodArgumentNotValidException e) {
        String msg = e.getBindingResult().getFieldError().getDefaultMessage();
        return Result.fail(400, msg);
    }

    @ExceptionHandler(Exception.class)
    public Result<Void> handleOther(Exception e) {
        log.error("系统异常", e);
        return Result.fail(500, "系统繁忙,请稍后再试");
    }
}

注意:@ExceptionHandler(Exception.class) 兜底时不要把堆栈信息返回给前端,否则会泄露内部实现细节。

六、参数校验

引入 spring-boot-starter-validation 后即可使用 Jakarta Bean Validation 注解。

public class UserCreateDTO {
    @NotBlank(message = "用户名不能为空")
    @Size(min = 3, max = 20, message = "用户名长度需在 3~20 之间")
    private String username;

    @Email(message = "邮箱格式不正确")
    private String email;
}

@PostMapping
public Result<Void> create(@Validated @RequestBody UserCreateDTO dto) { ... }
  • @Valid(JSR-303 标准)与 @Validated(Spring 扩展,支持分组校验)二选一。
  • 嵌套对象校验需在字段上加 @Valid 才会级联生效。

七、拦截器 vs 过滤器

维度 Filter(Servlet 规范) Interceptor(Spring MVC)
作用范围 所有请求(含静态资源) 仅 DispatcherServlet 处理的请求
能否拿到 Bean 不易(容器外) 可以(Spring 容器内)
能否拿到 Handler 可以
典型用途 编码、跨域、日志埋点 登录校验、权限、限流
public class AuthInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) {
        if (req.getHeader("Token") == null) {
            resp.setStatus(401);
            return false;      // 返回 false 即中断
        }
        return true;
    }
}

顺序Filter → preHandle → Controller → postHandle → afterCompletion

八、小结

  • Spring MVC = DispatcherServlet + 九大组件,核心是「映射 → 适配 → 执行 → 渲染」。
  • 参数绑定靠注解区分来源,JSON 用 @RequestBody,表单/查询用 @RequestParam
  • 生产环境必备三件套:全局异常处理 + 参数校验 + 登录拦截器