请求参数绑定与校验
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 |
| ServletModelAttributeMethodProcessor | POJO | @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实现声明式验证。
要点总结
- HandlerMethodArgumentResolver负责参数绑定,支持多种参数类型
- WebDataBinder处理请求参数到对象属性的类型转换
- HttpMessageConverter处理@RequestBody的JSON/XML解析
- @Valid/@Validated触发Bean Validation校验
- BindingResult封装校验结果,支持自定义错误处理
jwdev/articles/SPRINGMVC/专家/MVC核心源码执行流程\MVC核心源码执行流程\请求参数绑定与校验.md
📝 发现内容有误?点击此处直接编辑