Spring常見錯誤之Web嵌套對象校驗失效解決辦法
問題復現(xiàn)
當開發(fā)一個學籍管理系統(tǒng)時,我們會提供了一個 API 接口去添加學生的相關信息,學生中有個嵌套屬性聯(lián)系電話,其對象定義參考下面的代碼:
import lombok.Data;
import javax.validation.constraints.Size;
@Data
public class Student {
@Size(max = 10)
private String name;
private short age;
private Phone phone;
}
@Data
class Phone {
@Size(max = 10)
private String number;
}
這里我們也給 Phone 對象做了合法性要求(@Size(max = 10)),當我們使用下面的請求(請求 body 攜帶一個聯(lián)系電話信息超過 10 位),測試校驗會發(fā)現(xiàn)這個約束并不生效。
定義完對象后,我們再定義一個 Controller 去使用它,使用方法如下:
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Slf4j
@Validated
public class StudentController {
@RequestMapping(path = "students", method = RequestMethod.POST)
public void addStudent(@Validated @RequestBody Student student){
log.info("add new student: {}", student.toString());
//省略業(yè)務代碼
};
}
我們提供了一個支持學生信息添加的接口。啟動服務后,使用 IDEA 自帶的 HTTP Client 工具來發(fā)送下面的請求以添加一個學生:
POST http://localhost:8080/students
Content-Type: application/json
{
"name": "xiaoming",
"age": 10,
"phone": {"number":"12306123061230612306"}
}
發(fā)現(xiàn)校驗器并沒有生效。
案例解析
關于 student 本身的 Phone 類型成員是否校驗是在校驗過程中(即案例 1 中的代碼行 binder.validate(validationHints))決定的。
在校驗執(zhí)行時,首先會根據(jù) Student 的類型定義找出所有的校驗點,然后對 Student 對象實例執(zhí)行校驗,這個邏輯過程可以參考代碼 ValidatorImpl#validate:
@Override
public final <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups) {
//省略部分非關鍵代碼
Class<T> rootBeanClass = (Class<T>) object.getClass();
//獲取校驗對象類型的“信息”(包含“約束”)
BeanMetaData<T> rootBeanMetaData = beanMetaDataManager.getBeanMetaData( rootBeanClass );
if ( !rootBeanMetaData.hasConstraints() ) {
return Collections.emptySet();
}
//省略部分非關鍵代碼
//執(zhí)行校驗
return validateInContext( validationContext, valueContext, validationOrder );
}
這里語句"beanMetaDataManager.getBeanMetaData( rootBeanClass )"根據(jù) Student 類型組裝出 BeanMetaData,BeanMetaData 即包含了需要做的校驗(即 Constraint)。
在組裝 BeanMetaData 過程中,會根據(jù)成員字段是否標記了 @Valid 來決定(記錄)這個字段以后是否做級聯(lián)校驗,參考代碼 AnnotationMetaDataProvider#getCascadingMetaData:
private CascadingMetaDataBuilder getCascadingMetaData(Type type, AnnotatedElement annotatedElement,
Map<TypeVariable<?>, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData) {
return CascadingMetaDataBuilder.annotatedObject( type, annotatedElement.isAnnotationPresent( Valid.class ), containerElementTypesCascadingMetaData,
getGroupConversions( annotatedElement ) );
}
在上述代碼中"annotatedElement.isAnnotationPresent( Valid.class )"決定了 CascadingMetaDataBuilder#cascading 是否為 true。如果是,則在后續(xù)做具體校驗時,做級聯(lián)校驗,而級聯(lián)校驗的過程與宿主對象(即 Student)的校驗過程大體相同,即先根據(jù)對象類型獲取定義再來做校驗。
在當前案例代碼中,phone 字段并沒有被 @Valid 標記,所以關于這個字段信息的 cascading 屬性肯定是 false,因此在校驗 Student 時并不會級聯(lián)校驗它。
問題修正
從源碼級別了解了嵌套 Validation 失敗的原因后,我們會發(fā)現(xiàn),要讓嵌套校驗生效,解決的方法只有一種,就是加上 @Valid,修正代碼如下:
@Valid private Phone phone;
當修正完問題后,我們會發(fā)現(xiàn)校驗生效了。而如果此時去調(diào)試修正后的案例代碼,會看到 phone 字段 MetaData 信息中的 cascading 確實為 true 了,參考下圖:

總結(jié)
到此這篇關于Spring常見錯誤之Web嵌套對象校驗失效解決辦法的文章就介紹到這了,更多相關Spring Web嵌套對象校驗失效內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
JNDI在JavaEE中的角色_動力節(jié)點Java學院整理
這篇文章主要介紹了JNDI在JavaEE中的角色,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08
Java連接mysql數(shù)據(jù)庫以及mysql驅(qū)動jar包下載和使用方法
這篇文章主要給大家介紹了關于Java連接mysql數(shù)據(jù)庫以及mysql驅(qū)動jar包下載和使用方法,MySQL是一款常用的關系型數(shù)據(jù)庫,它的JDBC驅(qū)動程序使得我們可以通過Java程序連接MySQL數(shù)據(jù)庫進行數(shù)據(jù)操作,需要的朋友可以參考下2023-11-11
Maven的配置文件pom.xml詳解(含常用plugin)
pom.xml是Maven項目的核心配置文件,它是 項目對象模型 - Project Object Model(POM)的縮寫,本文我們將全面解析pom.xml,了解其結(jié)構(gòu)和屬性,以及如何使用它來管理項目,感興趣的朋友跟隨小編一起看看吧2024-08-08
Java實現(xiàn)二維碼QRCode的編碼和解碼與示例解析
本文主要介紹Java實現(xiàn)二維碼QRCode的編碼和解碼,這里給大家一個小示例以便理解,有需要的小伙伴可以參考下2016-08-08

