g("/saveUser")
@ResponseBody
//注意:如果方法中的参数是对象类型,则必须要在参数对象前面添加 @Validated
public R saveUser(@Validated @RequestBody UserDTO userDTO){
userDTO.setUserId(100);
return R.SUCCESS.setData(userDTO);
}
这种属于 DTO 级别的校验。在 spring-mvc 中,RequestResponseBodyMethodProcessor 是用于解析@RequestBody 标注的参数以及处理@ResponseBody 标注方法的返回值的。显然,执行参数校验的逻辑肯定就在解析参数的方法 resolveArgument()中。
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
parameter = parameter.nestedIfOptional();
//将请求数据封装到DTO对象中
Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
String name = Conventions.getVariableNameForParameter(parameter);
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
if (arg != null) {
// 执行数据校验
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
}
}
if (mavContainer != null) {
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
}
}
return adaptArgumentIfNecessary(arg, parameter);
}
}
可以看到,resolveArgument()调用了 validateIfApplicable()进行参数校验。
protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
// 获取参数注解,比如@RequestBody、@Valid、@Validated
Annotation[] annotations = parameter.getParameterAnnotations();
for (Annotation ann : annotations) {
// 先尝试获取@Validated 注解
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
//如果直接标注了@Validated,那么直接开启校验。
//如果没有,那么判断参数前是否有 Valid 起头的注解。
if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
//执行校验
binder.validate(validationHints);
break;
}
}
}
看到这里,大家应该能明白为什么这种场景下@Validated、@Valid 两个注解可以混用。我们接下来继续看 WebDataBinder.validate()实现。
最终发现底层最终还是调用了 Hibernate Validator 进行真正的校验处理。
404 等错误的统一处理
参考博客
参考
Spring Validation 实现原理及如何运用
SpringBoot 参数校验和国际化使用
@Valid 和@Validated 区别
Spring Validation 最佳实践及其实现原理,参数校验没那么简单!
出处:https://www.cnblogs.com/54chensongxia/p/14016179.html