Java Validation Api實現原理解析
前言:
涉及知識點:AOP、攔截器相關
功能主要實現類:因為bean validation只提供了接口并未實現,使用時需要加上一個provider的包,例如hibernate-validator
范圍: 注解:@Valid @RequestBudy
主要實現類:RequestResponseBodyMethodProcessor
處理器:HandlerMethodArgumentResolver
注解說明:
- @Valid:標準JSR-303規(guī)范的標記型注解,用來標記驗證屬性和方法返回值,進行級聯(lián)和遞歸校驗,@Valid可用于方法、字段、構造器和參數上
- @RequestBudy 請求的Body體,只能被讀取一次
RequestResponseBodyMethodProcessor 類說明:
// @since 3.1
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestBody.class);
}
// 類上或者方法上標注了@ResponseBody注解都行
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) || returnType.hasMethodAnnotation(ResponseBody.class));
}
// 這是處理入參封裝校驗的入口,也是本文關注的焦點
@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
// 它是支持`Optional`容器的
parameter = parameter.nestedIfOptional();
// 使用消息轉換器HttpInputMessage把request請求轉換出來,拿到值~~~
// 此處注意:比如本例入參是Person類,所以經過這里處理會生成一個空的Person對象出來(反射)
Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
// 獲取到入參的名稱,其實不叫形參名字,應該叫objectName給校驗時用的
// 請注意:這里的名稱是類名首字母小寫,并不是你方法里寫的名字。比如本利若形參名寫為personAAA,但是name的值還是person
// 但是注意:`parameter.getParameterName()`的值可是personAAA
String name = Conventions.getVariableNameForParameter(parameter);
// 只有存在binderFactory才會去完成自動的綁定、校驗~
// 此處web環(huán)境為:ServletRequestDataBinderFactory
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
// 顯然傳了參數才需要去綁定校驗嘛
if (arg != null) {
// 這里完成數據綁定+數據校驗~~~~~(綁定的錯誤和校驗的錯誤都會放進Errors里)
// Applicable:適合
validateIfApplicable(binder, parameter);
// 若有錯誤消息hasErrors(),并且僅跟著的一個參數不是Errors類型,Spring MVC會主動給你拋出MethodArgumentNotValidException異常
// 否則,調用者自行處理
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
}
}
// 把錯誤消息放進去 證明已經校驗出錯誤了~~~
// 后續(xù)邏輯會判斷MODEL_KEY_PREFIX這個key的~~~~
if (mavContainer != null) {
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
}
}
return adaptArgumentIfNecessary(arg, parameter);
}
// 校驗,如果合適的話。使用WebDataBinder,失敗信息最終也都是放在它身上~ 本方法是本文關注的焦點
// 入參:MethodParameter parameter
protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
// 拿到標注在此參數上的所有注解們(比如此處有@Valid和@RequestBody兩個注解)
Annotation[] annotations = parameter.getParameterAnnotations();
for (Annotation ann : annotations) {
// 先看看有木有@Validated
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
// 這個里的判斷是關鍵:可以看到標注了@Validated注解 或者注解名是以Valid打頭的 都會有效哦
//注意:這里可沒說必須是@Valid注解。實際上你自定義注解,名稱只要一Valid開頭都成~~~~~
if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
// 拿到分組group后,調用binder的validate()進行校驗~~~~
// 可以看到:拿到一個合適的注解后,立馬就break了~~~
// 所以若你兩個主機都標注@Validated和@Valid,效果是一樣滴~
Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
binder.validate(validationHints);
break;
}
}
}
...
}
可以看得,這個類應該是陌生的,它能夠處理@ResponseBody注解返回值;它還有另一個能力是:它能夠處理請求參數(當然也是標注了@RequestBody的JavaBean)
所以它既是個處理返回值的HandlerMethodReturnValueHandler,又是一個處理入參的HandlerMethodArgumentResolver。所以它命名為Processor而不是Resolver/Handler。
這是使用@RequestBody結合@Valid完成數據校驗的基本原理。其實當Spring MVC在處理@RequestPart注解入參數據時,也會執(zhí)行綁定、校驗的相關邏輯。對應處理器是RequestPartMethodArgumentResolver,原理大體上和這相似,它主要處理Multipart相關,本文忽略~
以上就是dui'y對于@Valid標注的@RequestBody的JavaBean的原理說明,敬請指點。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
- Springboot項目javax.validation使用方法詳解
- java validation 后臺參數驗證的使用詳解
- java使用Validation進行數據校驗的方式總結
- JAVA中通過Hibernate-Validation進行參數驗證
- JAVA中的字段校驗(validation)
- 使用javax.validation.constraints對請求體進行統(tǒng)一校驗
- Java參數校驗中validation和validator的區(qū)別詳解
- Java Validation Api如何實現自定義注解
- Javax Validation自定義注解進行身份證號校驗
- Java使用validation攔截非法提交的數據的方法實現
相關文章
Java中ReentrantLock和ReentrantReadWriteLock的原理
這篇文章主要介紹了Java中ReentrantLock和ReentrantReadWriteLock的原理,文章圍繞主題展開詳細的內容介紹,具有一定的參考價值,感興趣的小伙伴可以參考一下2022-09-09
spring-boot整合dubbo:Spring-boot-dubbo-starter
這篇文章主要介紹了spring-boot整合dubbo:Spring-boot-dubbo-starter的相關資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-05-05

