Java Validation Api实现原理解析
前言:
涉及知识点:AOP、拦截器相关
功能主要实现类:因为beanvalidation只提供了接口并未实现,使用时需要加上一个provider的包,例如hibernate-validator
范围:注解:@Valid@RequestBudy
主要实现类:RequestResponseBodyMethodProcessor
处理器:HandlerMethodArgumentResolver
注解说明:
- @Valid:标准JSR-303规范的标记型注解,用来标记验证属性和方法返回值,进行级联和递归校验,@Valid可用于方法、字段、构造器和参数上
- @RequestBudy请求的Body体,只能被读取一次
RequestResponseBodyMethodProcessor类说明:
//@since3.1 publicclassRequestResponseBodyMethodProcessorextendsAbstractMessageConverterMethodProcessor{ @Override publicbooleansupportsParameter(MethodParameterparameter){ returnparameter.hasParameterAnnotation(RequestBody.class); } //类上或者方法上标注了@ResponseBody注解都行 @Override publicbooleansupportsReturnType(MethodParameterreturnType){ return(AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(),ResponseBody.class)||returnType.hasMethodAnnotation(ResponseBody.class)); } //这是处理入参封装校验的入口,也是本文关注的焦点 @Override publicObjectresolveArgument(MethodParameterparameter,@NullableModelAndViewContainermavContainer, NativeWebRequestwebRequest,@NullableWebDataBinderFactorybinderFactory)throwsException{ //它是支持`Optional`容器的 parameter=parameter.nestedIfOptional(); //使用消息转换器HttpInputMessage把request请求转换出来,拿到值~~~ //此处注意:比如本例入参是Person类,所以经过这里处理会生成一个空的Person对象出来(反射) Objectarg=readWithMessageConverters(webRequest,parameter,parameter.getNestedGenericParameterType()); //获取到入参的名称,其实不叫形参名字,应该叫objectName给校验时用的 //请注意:这里的名称是类名首字母小写,并不是你方法里写的名字。比如本利若形参名写为personAAA,但是name的值还是person //但是注意:`parameter.getParameterName()`的值可是personAAA Stringname=Conventions.getVariableNameForParameter(parameter); //只有存在binderFactory才会去完成自动的绑定、校验~ //此处web环境为:ServletRequestDataBinderFactory if(binderFactory!=null){ WebDataBinderbinder=binderFactory.createBinder(webRequest,arg,name); //显然传了参数才需要去绑定校验嘛 if(arg!=null){ //这里完成数据绑定+数据校验~~~~~(绑定的错误和校验的错误都会放进Errors里) //Applicable:适合 validateIfApplicable(binder,parameter); //若有错误消息hasErrors(),并且仅跟着的一个参数不是Errors类型,SpringMVC会主动给你抛出MethodArgumentNotValidException异常 //否则,调用者自行处理 if(binder.getBindingResult().hasErrors()&&isBindExceptionRequired(binder,parameter)){ thrownewMethodArgumentNotValidException(parameter,binder.getBindingResult()); } } //把错误消息放进去证明已经校验出错误了~~~ //后续逻辑会判断MODEL_KEY_PREFIX这个key的~~~~ if(mavContainer!=null){ mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX+name,binder.getBindingResult()); } } returnadaptArgumentIfNecessary(arg,parameter); } //校验,如果合适的话。使用WebDataBinder,失败信息最终也都是放在它身上~本方法是本文关注的焦点 //入参:MethodParameterparameter protectedvoidvalidateIfApplicable(WebDataBinderbinder,MethodParameterparameter){ //拿到标注在此参数上的所有注解们(比如此处有@Valid和@RequestBody两个注解) Annotation[]annotations=parameter.getParameterAnnotations(); for(Annotationann:annotations){ //先看看有木有@Validated ValidatedvalidatedAnn=AnnotationUtils.getAnnotation(ann,Validated.class); //这个里的判断是关键:可以看到标注了@Validated注解或者注解名是以Valid打头的都会有效哦 //注意:这里可没说必须是@Valid注解。实际上你自定义注解,名称只要一Valid开头都成~~~~~ if(validatedAnn!=null||ann.annotationType().getSimpleName().startsWith("Valid")){ //拿到分组group后,调用binder的validate()进行校验~~~~ //可以看到:拿到一个合适的注解后,立马就break了~~~ //所以若你两个主机都标注@Validated和@Valid,效果是一样滴~ Objecthints=(validatedAnn!=null?validatedAnn.value():AnnotationUtils.getValue(ann)); Object[]validationHints=(hintsinstanceofObject[]?(Object[])hints:newObject[]{hints}); binder.validate(validationHints); break; } } } ... }
可以看得,这个类应该是陌生的,它能够处理@ResponseBody注解返回值;它还有另一个能力是:它能够处理请求参数(当然也是标注了@RequestBody的JavaBean)
所以它既是个处理返回值的HandlerMethodReturnValueHandler,又是一个处理入参的HandlerMethodArgumentResolver。所以它命名为Processor而不是Resolver/Handler。
这是使用@RequestBody结合@Valid完成数据校验的基本原理。其实当SpringMVC在处理@RequestPart注解入参数据时,也会执行绑定、校验的相关逻辑。对应处理器是RequestPartMethodArgumentResolver,原理大体上和这相似,它主要处理Multipart相关,本文忽略~
以上就是dui'y对于@Valid标注的@RequestBody的JavaBean的原理说明,敬请指点。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。