全部学科
NodeJS全栈
nodejs
Python全栈
python
小程序首页
📅 2026-05-18 10 分钟 ✍️ juanwangdev

请求参数绑定与校验

SpringMVC提供强大的参数绑定和校验机制,自动将请求数据转换为方法参数并进行校验。

参数绑定架构

核心接口

Java
public interface HandlerMethodArgumentResolver {
    boolean supportsParameter(MethodParameter parameter);

    @Nullable
    Object resolveArgument(MethodParameter parameter,
            ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest,
            WebDataBinderFactory binderFactory) throws Exception;
}

参数解析器链

Java
// RequestMappingHandlerAdapter初始化解析器链
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
    List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();

    // 基于注解的解析器
    resolvers.add(new RequestParamMethodArgumentResolver());
    resolvers.add(new RequestParamMapMethodArgumentResolver());
    resolvers.add(new PathVariableMethodArgumentResolver());
    resolvers.add(new MatrixVariableMethodArgumentResolver());
    resolvers.add(new RequestHeaderMethodArgumentResolver());
    resolvers.add(new RequestHeaderMapMethodArgumentResolver());
    resolvers.add(new RequestBodyMethodArgumentResolver());

    // 基于类型的解析器
    resolvers.add(new ServletRequestMethodArgumentResolver());
    resolvers.add(new ServletResponseMethodArgumentResolver());
    resolvers.add(new ModelMethodProcessor());

    // 自定义解析器
    resolvers.add(new RequestParamMethodArgumentResolver(true));

    return resolvers;
}

常用参数解析器

解析器支持参数类型注解
RequestParamMethodArgumentResolver基本类型、String@RequestParam
PathVariableMethodArgumentResolver路径变量@PathVariable
RequestBodyMethodArgumentResolver请求体对象@RequestBody
RequestHeaderMethodArgumentResolver请求头@RequestHeader
ServletModelAttributeMethodProcessorPOJO@ModelAttribute

参数绑定流程

Java
// InvocableHandlerMethod
protected Object[] getMethodArgumentValues(NativeWebRequest request,
        ModelAndViewContainer mavContainer, Object... providedArgs) {

    MethodParameter[] parameters = getMethodParameters();
    Object[] args = new Object[parameters.length];

    for (int i = 0; i < parameters.length; i++) {
        MethodParameter parameter = parameters[i];

        // 1. 查找支持的解析器
        HandlerMethodArgumentResolver resolver =
            this.argumentResolvers.getArgumentResolver(parameter);

        // 2. 执行参数解析
        args[i] = resolver.resolveArgument(parameter, mavContainer,
                                           request, this.dataBinderFactory);
    }
    return args;
}

@RequestParam解析

Java
public class RequestParamMethodArgumentResolver
        extends AbstractNamedValueMethodArgumentResolver {

    protected Object resolveName(String name, MethodParameter parameter,
            NativeWebRequest request) {
        // 获取请求参数值
        String[] paramValues = request.getParameterValues(name);

        if (paramValues != null) {
            if (paramValues.length == 1) {
                return paramValues[0];
            }
            return paramValues;
        }
        return null;
    }

    // 类型转换
    protected Object resolveArgumentInternal(MethodParameter parameter,
            NativeWebRequest request, String name) {
        Object arg = resolveName(name, parameter, request);
        // 通过TypeConverter进行类型转换
        return this.conversionService.convert(arg, parameter.getParameterType());
    }
}

@RequestBody解析

Java
public class RequestBodyMethodArgumentResolver
        extends AbstractMessageConverterMethodArgumentResolver {

    public Object resolveArgument(MethodParameter parameter,
            ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
            WebDataBinderFactory binderFactory) {

        HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);

        // 使用HttpMessageConverter解析请求体
        Object arg = readWithMessageConverters(servletRequest, parameter,
                                               parameter.getNestedGenericParameterType());

        // 参数校验
        validateIfApplicable(binder, parameter);
        return arg;
    }

    protected Object readWithMessageConverters(HttpServletRequest request,
            MethodParameter parameter, Type targetType) {
        // 遍历MessageConverter找到支持的转换器
        for (HttpMessageConverter<?> converter : this.messageConverters) {
            if (converter.canRead(targetType, contentType)) {
                return ((HttpMessageConverter<Object>) converter)
                    .read(targetType, inputMessage);
            }
        }
        return null;
    }
}

数据绑定与类型转换

WebDataBinder

Java
public class WebDataBinder extends DataBinder {
    // 绑定请求参数到目标对象
    public void bind(HttpServletRequest request) {
        MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);
        doBind(mpvs);
    }

    // 类型转换
    protected void doBind(MutablePropertyValues mpvs) {
        checkAllowedFields(mpvs);
        checkRequiredFields(mpvs);
        applyPropertyValues(mpvs);
    }
}

ConversionService

Java
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addFormatters(FormatterRegistry registry) {
        // 注册自定义转换器
        registry.addConverter(new StringToDateConverter());
        registry.addConverter(new StringToEnumConverter());
    }
}

// 自定义转换器
public class StringToDateConverter implements Converter<String, Date> {
    @Override
    public Date convert(String source) {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        return format.parse(source);
    }
}

参数校验机制

@Valid与@Validated

Java
// Controller方法
@PostMapping("/users")
public ResponseEntity<User> createUser(@Valid @RequestBody UserDTO userDTO) {
    return ResponseEntity.ok(userService.create(userDTO));
}

// DTO定义
public class UserDTO {
    @NotBlank(message = "用户名不能为空")
    @Size(min = 2, max = 20, message = "用户名长度2-20")
    private String username;

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

    @Min(value = 18, message = "年龄不能小于18岁")
    private Integer age;
}

校验流程

Java
// ValidatingWebDataBinder
protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
    Annotation[] annotations = parameter.getParameterAnnotations();
    for (Annotation ann : annotations) {
        if (ann.annotationType().getSimpleName().startsWith("Valid")) {
            binder.validate();
            break;
        }
    }
}

// 数据绑定后校验
public void validate() {
    Validator validator = getValidator();
    if (validator != null) {
        validator.validate(getTarget(), getBindingResult());
    }
}

校验结果处理

Java
@PostMapping("/users")
public ResponseEntity<?> createUser(
        @Valid @RequestBody UserDTO userDTO,
        BindingResult bindingResult) {

    // 手动处理校验结果
    if (bindingResult.hasErrors()) {
        List<String> errors = bindingResult.getFieldErrors().stream()
            .map(FieldError::getDefaultMessage)
            .collect(Collectors.toList());
        return ResponseEntity.badRequest().body(errors);
    }

    return ResponseEntity.ok(userService.create(userDTO));
}

自定义校验器

Java
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PhoneValidator.class)
public @interface Phone {
    String message() default "手机号格式不正确";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

public class PhoneValidator implements ConstraintValidator<Phone, String> {
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return value != null && value.matches("^1[3-9]\\d{9}$");
    }
}

参数绑定通过HandlerMethodArgumentResolver链实现,校验通过Bean Validation API实现声明式验证。

要点总结

  1. HandlerMethodArgumentResolver负责参数绑定,支持多种参数类型
  2. WebDataBinder处理请求参数到对象属性的类型转换
  3. HttpMessageConverter处理@RequestBody的JSON/XML解析
  4. @Valid/@Validated触发Bean Validation校验
  5. BindingResult封装校验结果,支持自定义错误处理

jwdev/articles/SPRINGMVC/专家/MVC核心源码执行流程\MVC核心源码执行流程\请求参数绑定与校验.md

📝 发现内容有误?点击此处直接编辑

← 上一篇 拦截器执行顺序
下一篇 → HandlerAdapter
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库