基于本文回答
0
评论

Spring MVC 是如何进行数据绑定的?

知识点图片

Spring MVC 的数据绑定(Data Binding)是将 HTTP 请求中的参数(通常是字符串形式,如 URL 参数、Form 表单数据)自动转换并赋值给 Java 方法参数(如 POJO 对象、基本数据类型)的过程。

这个过程的核心组件是 WebDataBinder

以下是 Spring MVC 数据绑定的详细工作流程和核心机制:

1. 核心流程概览

当一个 HTTP 请求到达 DispatcherServlet 后,流程如下:

  1. HandlerAdapter 调用: DispatcherServlet 将请求委托给 RequestMappingHandlerAdapter 来执行具体的 Controller 方法。
  2. 参数解析 (Argument Resolution): Adapter 需要确定如何填充 Controller 方法的参数。它会遍历 HandlerMethodArgumentResolver 列表,找到支持该参数类型的解析器(例如 ServletModelAttributeMethodProcessor 用于处理 POJO 对象)。
  3. 创建 Binder: 如果参数需要绑定(例如是一个 JavaBean),解析器会创建一个 WebDataBinder 实例。
  4. 数据注入: WebDataBinder 利用 PropertyAccessor(通常是 BeanWrapper)将请求中的参数值注入到目标对象中。
  5. 类型转换: 由于 HTTP 参数都是 String,而 Java 字段可能是 Integer、Date 等,Binder 会调用 ConversionService (或旧式的 PropertyEditor) 进行类型转换。
  6. 校验 (Validation): 如果参数上标注了 @Valid@Validated,Binder 会执行校验逻辑。
  7. 返回结果: 绑定完成的对象被传给 Controller 方法。

2. 关键组件详解

A. WebDataBinder (核心)

它是 DataBinder 的子类,专门用于 Web 环境。它的作用就像一个“指挥官”:

  • 它持有目标对象(Target Object,即你要绑定的 POJO)。
  • 它持有数据源(Request Parameters)。
  • 它负责调用转换器和校验器。

B. HandlerMethodArgumentResolver (参数解析器)

Spring MVC 使用策略模式来解析不同类型的参数。

  • 简单类型: 如果参数是 int, String 等,通常由 RequestParamMethodArgumentResolver 处理。
  • 对象类型: 如果参数是自定义 POJO(如 User user),通常由 ServletModelAttributeMethodProcessor 处理。正是这个处理器触发了 WebDataBinder 的创建和绑定过程。

C. ConversionService & PropertyEditor (类型转换)

这是将 "String" 变为 "Object" 的底层机制。

  • PropertyEditor (旧机制): 基于 JDK 的 java.beans.PropertyEditor。它是非线程安全的,通常为每个请求创建一个。Spring 内置了许多 Editor(如 CustomDateEditor)。
  • Converter / Formatter (新机制): Spring 3.0 引入。
    • Converter<S, T>: 将类型 S 转换为类型 T(通用)。
    • Formatter<T>: 专门处理 String 和 Object 之间的转换,支持国际化(如日期、货币格式)。
    • ConversionService: 统一管理这些 Converter 和 Formatter。WebDataBinder 会委托它进行类型转换。

D. BeanWrapper

WebDataBinder 并不直接操作对象属性,而是通过 BeanWrapperBeanWrapper 提供了设置和获取 Bean 属性(getter/setter)的低级 API,它利用 Java 反射机制来完成实际的赋值操作。


3. 绑定步骤细节 (以表单提交到 POJO 为例)

假设有一个 User 类和一个请求 POST /save?name=zhangsan&age=20

  1. 匹配字段: WebDataBinder 获取请求参数名(name, age)。
  2. 查找属性: 它在 User 对象中查找对应的属性(通常通过 setter 方法,如 setName, setAge)。
  3. 类型转换:
    • name (String) -> user.name (String): 直接赋值。
    • age (String "20") -> user.age (Integer): WebDataBinder 发现类型不匹配,调用 ConversionService 将字符串 "20" 转换为整数 20。
  4. 赋值: 通过反射调用 user.setAge(20)
  5. 错误处理: 如果转换失败(例如 age=abc),错误信息会被放入 BindingResult 对象中,而不是直接抛出异常(除非没有定义 BindingResult 参数)。

4. 特殊情况:@RequestBody (JSON/XML)

需要区分的是,@RequestBody 的处理机制与上述普通的表单绑定不同

  • 普通绑定 (Form/URL): 使用 WebDataBinder,基于字段名匹配,处理 application/x-www-form-urlencoded 数据。
  • @RequestBody (JSON/XML): 使用 HttpMessageConverter (如 Jackson, Gson)。
    • 它不使用 WebDataBinder 进行逐个字段的 setter 调用。
    • 它直接读取 HTTP Request Body 的输入流,利用序列化库(如 Jackson 的 ObjectMapper)将整个 JSON 字符串反序列化为 Java 对象。

5. 总结

Spring MVC 的数据绑定可以概括为:

Request 参数 -> HandlerMethodArgumentResolver (识别参数) -> WebDataBinder (构建绑定上下文) -> ConversionService (类型转换 String to Object) -> BeanWrapper (反射赋值) -> Target Object (最终对象)。

右滑查看面试常问