Java Validation Api實(shí)現(xiàn)原理解析
前言:
涉及知識(shí)點(diǎn):AOP、攔截器相關(guān)
功能主要實(shí)現(xiàn)類:因?yàn)閎ean validation只提供了接口并未實(shí)現(xiàn),使用時(shí)需要加上一個(gè)provider的包,例如hibernate-validator
范圍: 注解:@Valid @RequestBudy
主要實(shí)現(xiàn)類:RequestResponseBodyMethodProcessor
處理器:HandlerMethodArgumentResolver
注解說(shuō)明:
- @Valid:標(biāo)準(zhǔn)JSR-303規(guī)范的標(biāo)記型注解,用來(lái)標(biāo)記驗(yàn)證屬性和方法返回值,進(jìn)行級(jí)聯(lián)和遞歸校驗(yàn),@Valid可用于方法、字段、構(gòu)造器和參數(shù)上
- @RequestBudy 請(qǐng)求的Body體,只能被讀取一次
RequestResponseBodyMethodProcessor 類說(shuō)明:
// @since 3.1 public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor { @Override public boolean supportsParameter(MethodParameter parameter) { return parameter.hasParameterAnnotation(RequestBody.class); } // 類上或者方法上標(biāo)注了@ResponseBody注解都行 @Override public boolean supportsReturnType(MethodParameter returnType) { return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) || returnType.hasMethodAnnotation(ResponseBody.class)); } // 這是處理入?yún)⒎庋b校驗(yàn)的入口,也是本文關(guān)注的焦點(diǎn) @Override public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { // 它是支持`Optional`容器的 parameter = parameter.nestedIfOptional(); // 使用消息轉(zhuǎn)換器HttpInputMessage把request請(qǐng)求轉(zhuǎn)換出來(lái),拿到值~~~ // 此處注意:比如本例入?yún)⑹荘erson類,所以經(jīng)過(guò)這里處理會(huì)生成一個(gè)空的Person對(duì)象出來(lái)(反射) Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType()); // 獲取到入?yún)⒌拿Q,其實(shí)不叫形參名字,應(yīng)該叫objectName給校驗(yàn)時(shí)用的 // 請(qǐng)注意:這里的名稱是類名首字母小寫,并不是你方法里寫的名字。比如本利若形參名寫為personAAA,但是name的值還是person // 但是注意:`parameter.getParameterName()`的值可是personAAA String name = Conventions.getVariableNameForParameter(parameter); // 只有存在binderFactory才會(huì)去完成自動(dòng)的綁定、校驗(yàn)~ // 此處web環(huán)境為:ServletRequestDataBinderFactory if (binderFactory != null) { WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name); // 顯然傳了參數(shù)才需要去綁定校驗(yàn)嘛 if (arg != null) { // 這里完成數(shù)據(jù)綁定+數(shù)據(jù)校驗(yàn)~~~~~(綁定的錯(cuò)誤和校驗(yàn)的錯(cuò)誤都會(huì)放進(jìn)Errors里) // Applicable:適合 validateIfApplicable(binder, parameter); // 若有錯(cuò)誤消息hasErrors(),并且僅跟著的一個(gè)參數(shù)不是Errors類型,Spring MVC會(huì)主動(dòng)給你拋出MethodArgumentNotValidException異常 // 否則,調(diào)用者自行處理 if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) { throw new MethodArgumentNotValidException(parameter, binder.getBindingResult()); } } // 把錯(cuò)誤消息放進(jìn)去 證明已經(jīng)校驗(yàn)出錯(cuò)誤了~~~ // 后續(xù)邏輯會(huì)判斷MODEL_KEY_PREFIX這個(gè)key的~~~~ if (mavContainer != null) { mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult()); } } return adaptArgumentIfNecessary(arg, parameter); } // 校驗(yàn),如果合適的話。使用WebDataBinder,失敗信息最終也都是放在它身上~ 本方法是本文關(guān)注的焦點(diǎn) // 入?yún)ⅲ篗ethodParameter parameter protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) { // 拿到標(biāo)注在此參數(shù)上的所有注解們(比如此處有@Valid和@RequestBody兩個(gè)注解) Annotation[] annotations = parameter.getParameterAnnotations(); for (Annotation ann : annotations) { // 先看看有木有@Validated Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class); // 這個(gè)里的判斷是關(guān)鍵:可以看到標(biāo)注了@Validated注解 或者注解名是以Valid打頭的 都會(huì)有效哦 //注意:這里可沒(méi)說(shuō)必須是@Valid注解。實(shí)際上你自定義注解,名稱只要一Valid開(kāi)頭都成~~~~~ if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) { // 拿到分組group后,調(diào)用binder的validate()進(jìn)行校驗(yàn)~~~~ // 可以看到:拿到一個(gè)合適的注解后,立馬就break了~~~ // 所以若你兩個(gè)主機(jī)都標(biāo)注@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; } } } ... }
可以看得,這個(gè)類應(yīng)該是陌生的,它能夠處理@ResponseBody注解返回值;它還有另一個(gè)能力是:它能夠處理請(qǐng)求參數(shù)(當(dāng)然也是標(biāo)注了@RequestBody的JavaBean)
所以它既是個(gè)處理返回值的HandlerMethodReturnValueHandler,又是一個(gè)處理入?yún)⒌腍andlerMethodArgumentResolver。所以它命名為Processor而不是Resolver/Handler。
這是使用@RequestBody結(jié)合@Valid完成數(shù)據(jù)校驗(yàn)的基本原理。其實(shí)當(dāng)Spring MVC在處理@RequestPart注解入?yún)?shù)據(jù)時(shí),也會(huì)執(zhí)行綁定、校驗(yàn)的相關(guān)邏輯。對(duì)應(yīng)處理器是RequestPartMethodArgumentResolver,原理大體上和這相似,它主要處理Multipart相關(guān),本文忽略~
以上就是dui'y對(duì)于@Valid標(biāo)注的@RequestBody的JavaBean的原理說(shuō)明,敬請(qǐng)指點(diǎn)。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
使用Mybatis Plus整合多數(shù)據(jù)源和讀寫分離的詳細(xì)過(guò)程
這篇文章主要介紹了Mybatis Plus整合多數(shù)據(jù)源和讀寫分離的詳細(xì)過(guò)程,mybatisplus可以整合阿里的分布式事務(wù)組件seata,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧2021-09-09java 出現(xiàn)Zipexception 異常的解決辦法
這篇文章主要介紹了java 出現(xiàn)Zipexception 異常的解決辦法的相關(guān)資料,出現(xiàn) java.util.zip.ZipException: error in opening zip file 異常的原因及解決方法,需要的朋友可以參考下2017-08-08SpringBoot中動(dòng)態(tài)數(shù)據(jù)源是實(shí)現(xiàn)與用途
這篇文章主要是來(lái)和大家討論一下SpringBoot中動(dòng)態(tài)數(shù)據(jù)源是實(shí)現(xiàn)與用途,文中的示例代碼簡(jiǎn)潔易懂,具有一定的學(xué)習(xí)價(jià)值,感興趣的可以了解一下2023-08-08Java元素排序Comparable與Comparator的區(qū)別
這篇文章主要介紹了Java元素排序Comparable與Comparator的區(qū)別,二者都是頂級(jí)的接口,但擁有的方法和用法是不同的,下面我們分別來(lái)看看具體是怎樣的區(qū)別吧2022-05-05解決若依pageHelper在動(dòng)態(tài)切換數(shù)據(jù)源問(wèn)題
這篇文章主要介紹了解決pageHelper在動(dòng)態(tài)切換數(shù)據(jù)源問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01Java中Future和FutureTask的示例詳解及使用
Java中的Future和FutureTask通常和線程池搭配使用,用來(lái)獲取線程池返回執(zhí)行后的返回值,下面這篇文章主要給大家介紹了關(guān)于Java中Future和FutureTask使用的相關(guān)資料,需要的朋友可以參考下2021-11-11淺談Maven安裝及環(huán)境配置出錯(cuò)的解決辦法
這篇文章主要介紹了淺談Maven安裝及環(huán)境配置出錯(cuò)的解決辦法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09IDEA不識(shí)別Java文件:文件變橙色&顯示后綴名.java的解決
這篇文章主要介紹了IDEA不識(shí)別Java文件:文件變橙色&顯示后綴名.java的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03