深入了解數(shù)據(jù)校驗(yàn):Bean Validation 2.0(JSR380)

每篇一句

吾皇一日不退役档泽,爾等都是臣子


<center>對(duì)Spring感興趣可掃碼加入wx群:Java高工全庸、架構(gòu)師3群(文末有二維碼)</center>


前言

前幾篇文章在講Spring的數(shù)據(jù)綁定的時(shí)候澎迎,多次提到過(guò)數(shù)據(jù)校驗(yàn)夸盟」喙耄可能有人認(rèn)為數(shù)據(jù)校驗(yàn)?zāi)K并不是那么的重要萤衰,因?yàn)橛簿幋a都可以做堕义。若是這么想的話,那就大錯(cuò)特錯(cuò)了~
前面講解DataBinder的時(shí)候一個(gè)小細(xì)節(jié)脆栋,它所在的包是:org.springframework.validation倦卖,并且在分析源碼的時(shí)候能看到DataBinder它不僅能夠完成數(shù)據(jù)綁定,也提供了對(duì)數(shù)據(jù)校驗(yàn)的支持且還保存了校驗(yàn)結(jié)果椿争。

我以數(shù)據(jù)綁定DataBinder為引子引出了數(shù)據(jù)校驗(yàn)這一塊怕膛,是想表明它的重要性。連Java都把它抽象成了JSR標(biāo)準(zhǔn)進(jìn)行提出秦踪,so我認(rèn)為這塊是必修課褐捻,有必要了解本章的內(nèi)容掸茅。

為什么要有數(shù)據(jù)校驗(yàn)?

數(shù)據(jù)校驗(yàn) 是非常常見(jiàn)的工作柠逞,在日常的開(kāi)發(fā)中貫穿于代碼的各個(gè)層次昧狮,從上層的View層到底層的數(shù)據(jù)層。

在此處有必要再?gòu)?qiáng)調(diào)一句:前面說(shuō)了數(shù)據(jù)綁定并不屬于Spring MVC的專利边苹,同樣的數(shù)據(jù)校驗(yàn)也不是只會(huì)發(fā)生在web層陵且,它可以在任意一層,從后面的示例中你會(huì)有更深的理解

在任何時(shí)候个束,當(dāng)你要處理一個(gè)應(yīng)用程序的業(yè)務(wù)邏輯慕购,數(shù)據(jù)校驗(yàn)是你必須要考慮和面對(duì)的事情。應(yīng)用程序必須通過(guò)某種手段來(lái)確保輸入進(jìn)來(lái)的數(shù)據(jù)從語(yǔ)義上來(lái)講是正確的(比如生日必須是過(guò)去時(shí)茬底,年齡必須>0等等~)沪悲。

我們知道通常情況下程序肯定是分層的,不同的層一般由不同的人來(lái)開(kāi)發(fā)阱表。若你是一個(gè)有經(jīng)驗(yàn)的程序員殿如, 我相信你肯定見(jiàn)過(guò)在不同的層了都出現(xiàn)了相同的校驗(yàn)代碼,這就是某種意義上的垃圾代碼最爬。

public String queryValueByKey(String parmTemplateCode, String conditionName, String conditionKey, String resultName) {
    checkNotNull(parmTemplateCode, "parmTemplateCode not null");
    checkNotNull(conditionName, "conditionName not null");
    checkNotNull(conditionKey, "conditionKey not null");
    checkNotNull(resultName, "resultName not null");
    ...
}

從這個(gè)簡(jiǎn)單的方法入?yún)⑿r?yàn)至少能發(fā)現(xiàn)如下問(wèn)題:

  1. 需要寫大量的代碼來(lái)進(jìn)行參數(shù)驗(yàn)證涉馁。(這種代碼多了就算垃圾代碼)
  2. 需要通過(guò)注釋來(lái)知道每個(gè)入?yún)⒌募s束是什么(否則別人咋看得懂)
  3. 每個(gè)程序員做參數(shù)驗(yàn)證的方式不一樣,參數(shù)驗(yàn)證不通過(guò)拋出的異常也不一樣(后期幾乎沒(méi)法維護(hù))

如上會(huì)導(dǎo)致代碼冗余和一些管理的問(wèn)題(代碼量越大爱致,管理起來(lái)維護(hù)起來(lái)就越困難)烤送,比如說(shuō)語(yǔ)義的一致性等。為了避免這樣的情況發(fā)生糠悯,最好是將驗(yàn)證邏輯與相應(yīng)的域模型(領(lǐng)域模型的概念)進(jìn)行綁定帮坚,這就是本文提供的一個(gè)新思路(其實(shí)是JavaEE提供的思路)

為了解決這個(gè)問(wèn)題,Bean ValidationJavaBean 驗(yàn)證定義了相應(yīng)的元數(shù)據(jù)模型和 API互艾。默認(rèn)的元數(shù)據(jù)是 各種Java Annotations试和,當(dāng)然也支持xml方式并且你也可以擴(kuò)展~
可以說(shuō)Bean ValidationJavaBean的一個(gè)拓展,它可以布局于任意一層代碼纫普,不局限于Web應(yīng)用還是端應(yīng)用阅悍。

Java Bean Validation

JSR是Java Specification Requests的縮寫,意思是Java 規(guī)范提案局嘁。關(guān)于數(shù)據(jù)校驗(yàn)這塊溉箕,最新的是JSR380,也就是我們常說(shuō)的Bean Validation 2.0悦昵。

Bean Validation 2.0 是JSR第380號(hào)標(biāo)準(zhǔn)肴茄。該標(biāo)準(zhǔn)連接如下:https://www.jcp.org/en/egc/view?id=380
Bean Validation的主頁(yè):http://beanvalidation.org
Bean Validation的參考實(shí)現(xiàn):https://github.com/hibernate/hibernate-validator

Bean Validation是一個(gè)通過(guò)配置注解來(lái)驗(yàn)證參數(shù)的框架,它包含兩部分Bean Validation API(規(guī)范)和Hibernate Validator(實(shí)現(xiàn))但指。
Bean Validation是Java定義的一套基于注解/xml的數(shù)據(jù)校驗(yàn)規(guī)范寡痰,目前已經(jīng)從JSR 303的1.0版本升級(jí)到JSR 349的1.1版本抗楔,再到JSR 380的2.0版本(2.0完成于2017.08),已經(jīng)經(jīng)歷了三個(gè)版本(我截圖如下:)


現(xiàn)在絕大多數(shù)coder使用者其實(shí)都還在使用Bean Validation 1.1拦坠,畢竟一般來(lái)說(shuō)它已經(jīng)夠用了~
本文會(huì)介紹Bean Validation 2.0提供的一些實(shí)用的新東西连躏,畢竟Java8現(xiàn)在已成為主流,完全可以使用了~

簡(jiǎn)單Demo示例

要想使用它贞滨,首先就得導(dǎo)包嘛~根據(jù)經(jīng)驗(yàn)入热,和JCache類似Java只提供了規(guī)范,并沒(méi)有提供實(shí)現(xiàn)晓铆,所以我們可以先找到它的API包然后導(dǎo)入:

<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <!-- <version>1.1.0.Final</version> -->
    <version>2.0.1.Final</version>
</dependency>

關(guān)于版本之間的差異其實(shí)不是本文說(shuō)明的重點(diǎn)勺良,畢竟2.0做到了很好的向下兼容,使用起來(lái)是無(wú)縫的骄噪。
但是本處還是給個(gè)1.1版本和2.0.1的截圖尚困,感官上簡(jiǎn)單對(duì)比一下區(qū)別:



兼容性表格

Bean Validation Hibernate Validation JDK Spring Boot
1.1 5.4 + 6+ 1.5.x
2.0 6.0 + 8+ 2.0.x

關(guān)于Bean Validation 2.0的關(guān)注點(diǎn)(新特性)

因?yàn)?.0推出的時(shí)間確實(shí)不算長(zhǎng),so此處我把一些重要的關(guān)注點(diǎn)列舉如下:

  1. 對(duì)Java的最低版本要求是Java 8
  2. 支持容器的校驗(yàn)链蕊,通過(guò)TYPE_USE類型的注解實(shí)現(xiàn)對(duì)容器內(nèi)容的約束:List<@Email String>
  3. 支持日期/時(shí)間的校驗(yàn)事甜,@Past@Future
  4. 拓展元數(shù)據(jù)(新增注解):@Email,@NotEmpty滔韵,@NotBlank逻谦,@Positive, @PositiveOrZero陪蜻,@Negative跨跨,@NegativeOrZero,@PastOrPresent和@FutureOrPresent
    1. 像@Email囱皿、@NotEmpty、@NotBlank之前是Hibernate額外提供的忱嘹,2.0標(biāo)準(zhǔn)后hibernate自動(dòng)退位讓賢并且標(biāo)注為過(guò)期了
  5. Bean Validation 2.0的唯一實(shí)現(xiàn)為Hibernate Validator嘱腥。(其實(shí)還有Apache BVal,但是你懂的拘悦,forget it)
  6. 對(duì)于Hibernate Validator齿兔,它自己也擴(kuò)展了一些注解支持。
    1. 6.0以上版本新增(對(duì)應(yīng)標(biāo)準(zhǔn)2.0版本):@UniqueElements础米、@ISBN分苇、@CodePointLength
    2. 6.0以下版本可以使用的: @URL、@ScriptAssert屁桑、@SafeHtml医寿、@Range、@ParameterScriptAssert蘑斧、@Mod11Check靖秩、@Mod10Check须眷、@LuhnCheck、@Length沟突、@EAN花颗、@Currency、@CreditCardNumber惠拭、@ConstraintComposition扩劝、
    3. Hibernate Validator默認(rèn)會(huì)校驗(yàn)完所有的屬性,然后返回所有的驗(yàn)證失敗信息职辅。開(kāi)啟fail fast mode后棒呛,只要有一個(gè)驗(yàn)證失敗,則返回驗(yàn)證失敗信息罐农。

so条霜,對(duì)于Java Bean Validation的實(shí)現(xiàn)落地產(chǎn)品就沒(méi)啥好選的,導(dǎo)入Hibernate Validator(最新版本)吧:

<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.0.17.Final</version>
</dependency>

==小細(xì)節(jié):==


可以看到涵亏,導(dǎo)入了hibernate-validator就必要再自己導(dǎo)入Java Bean ValidationAPI了宰睡,因此建議不用再手動(dòng)導(dǎo)入API,交給內(nèi)部來(lái)管理依賴气筋。

定義一個(gè)待校驗(yàn)的普通JavaBean:

@Getter
@Setter
@ToString
public class Person {

    // 錯(cuò)誤消息message是可以自定義的
    @NotNull(message = "名字不能為null")
    public String name;
    @Positive
    public Integer age;

    @NotNull
    @NotEmpty
    private List<@Email String> emails;
    @Future
    private Date start;

}

書寫測(cè)試用例:

    public static void main(String[] args) {
        Person person = new Person();
        //person.setName("fsx");
        person.setAge(-1);
        // email校驗(yàn):雖然是List都可以校驗(yàn)哦
        person.setEmails(Arrays.asList("fsx@gmail.com", "baidu@baidu.com", "aaa.com"));
        //person.setStart(new Date()); //start 需要是一個(gè)將來(lái)的時(shí)間: Sun Jul 21 10:45:03 CST 2019
        //person.setStart(new Date(System.currentTimeMillis() + 10000)); //校驗(yàn)通過(guò)

        // 對(duì)person進(jìn)行校驗(yàn)然后拿到結(jié)果(顯然使用時(shí)默認(rèn)的校驗(yàn)器)   會(huì)保留下校驗(yàn)失敗的消息
        Set<ConstraintViolation<Person>> result = Validation.buildDefaultValidatorFactory().getValidator().validate(person);
        // 對(duì)結(jié)果進(jìn)行遍歷輸出
        result.stream().map(v -> v.getPropertyPath() + " " + v.getMessage() + ": " + v.getInvalidValue())
                .forEach(System.out::println);
    }

運(yùn)行拆内,報(bào)錯(cuò)啦:

Caused by: java.lang.ClassNotFoundException: javax.el.ELManager
    at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
...

可以看到運(yùn)行必須依賴于javax.el這個(gè)包。(其實(shí)我是比較費(fèi)解的宠默,為何校驗(yàn)框架非得依賴它呢麸恍?有小伙伴可以幫忙解釋一下嗎?)

那行搀矫,導(dǎo)入依賴javax.el以及它的實(shí)現(xiàn):

<!-- 注意這里導(dǎo)入的是Apr, 2013發(fā)布的el3.x的版本抹沪,但是glassfish并沒(méi)有對(duì)此版本進(jìn)行支持了  當(dāng)然tomcat肯定是支持的 -->
<dependency>
    <groupId>javax.el</groupId>
    <artifactId>javax.el-api</artifactId>
    <version>3.0.1-b06</version>
</dependency>
<!-- servlet容器大都對(duì)el有實(shí)現(xiàn)(支持jsp的都對(duì)此有實(shí)現(xiàn)),比如tomcat/glassfish等 -->
<dependency>
    <groupId>org.glassfish.web</groupId>
    <artifactId>javax.el</artifactId>
    <version>2.2.6</version>
</dependency>

需要注意的是瓤球,網(wǎng)上大都建議導(dǎo)入org.glassfish.web包融欧。但是EL3.0后它并沒(méi)有再提供支持了,因此我個(gè)人是不建議使用它卦羡,而是使用下面tomcat的實(shí)現(xiàn)的~

關(guān)于EL的實(shí)現(xiàn)此處啰嗦一句:JavaEE并沒(méi)有提供el的實(shí)現(xiàn)噪馏,需要容器自行提供,比如上面你想要導(dǎo)入最為流行的tomcat绿饵,你可以導(dǎo)入如下jar即可:

<!-- 嵌入式的tomcat -->
<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-el</artifactId>
    <version>9.0.22</version>
</dependency>
<!-- 傳統(tǒng)的tomcat(需要注意的是:傳統(tǒng)的tomcat這種jar是不需要你手動(dòng)導(dǎo)入的欠肾,tomcat自帶的) -->
<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-jasper-el</artifactId>
    <version>9.0.22</version>
    <scope>provided</scope>
</dependency>

此處還需要說(shuō)明一點(diǎn)的是:嵌入式tomcat(比如SpringBoot環(huán)境)若要使用時(shí)需要顯示導(dǎo)入的。但是傳統(tǒng)tomcat中你若要使用是不用自己導(dǎo)入的(tomcat自帶此jar)拟赊。

但是刺桃,但是,但是自從tomcat8.5后不再自帶jsper-el的包了吸祟,需要手動(dòng)導(dǎo)入虏肾。(tomcat7還是有的~

==最佳實(shí)踐:==
一般來(lái)說(shuō)廓啊,javax.el-api以及validation-api都是沒(méi)有必要單獨(dú)導(dǎo)入的,第三方包都會(huì)自帶封豪。所以絕大數(shù)情況下谴轮,我們只需要這么導(dǎo)入即可正常work,形如下面這樣非常趕緊整潔:

<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.0.17.Final</version>
</dependency>
<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-el</artifactId>
    <version>9.0.22</version>
</dependency>
此處可能有伙伴會(huì)問(wèn):為何自己在使用的時(shí)候從來(lái)都沒(méi)有導(dǎo)入過(guò)EL相關(guān)Jar包吹埠,也能正常數(shù)據(jù)校驗(yàn)?zāi)兀?/h6>

答:那是因?yàn)榻^大多數(shù)情況下你使用@Valid是使用在Spring MVC上第步,它是不依賴于EL方式的,下篇文章會(huì)詳細(xì)說(shuō)明關(guān)于數(shù)據(jù)校驗(yàn)在Spring上的使用缘琅。而本文主要還是講解API的方式~



經(jīng)過(guò)一番導(dǎo)包后粘都,再次運(yùn)行打印如下(方式一、方式二結(jié)果一致):

name名字不能為null: null //  此處錯(cuò)誤消息是自己的自定義內(nèi)容
age必須是正數(shù): -1
emails[2].<list element>不是一個(gè)合法的電子郵件地址: aaa.com

這樣通過(guò)API調(diào)用的方式就完成了對(duì)這個(gè)JavaBean的屬性校驗(yàn)~

核心API分析

Validation

官方給它的定義為:This class is the entry point for Bean Validation.它作為校驗(yàn)的入口刷袍,有三種方式來(lái)啟動(dòng)它:

  1. 最簡(jiǎn)單方式:使用默認(rèn)的ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); 雖然是默認(rèn)的單也會(huì)有如下2種情況:
    1. 若使用了xml配置了一個(gè)provider翩隧,那就會(huì)使用這個(gè)provider來(lái)提供Factory
    2. 若沒(méi)有xml或者xml力沒(méi)有配置provider,那就是用默認(rèn)的ValidationProviderResolver實(shí)現(xiàn)類來(lái)處理
  2. 方式二:選擇自定義的ValidationProviderResolver來(lái)跟XML配置邏輯選出一個(gè)ValidationProvider來(lái)呻纹。大致代碼如下:
Configuration configuration = Validation.byDefaultProvider()
        .providerResolver(new MyResolverStrategy()) // 自定義一個(gè)ValidationProviderResolver的實(shí)現(xiàn)類
        .configure();
ValidatorFactory factory = configuration.buildValidatorFactory();
  1. 第三種方式就更加自由了:你可以直接提供一個(gè)類型安全ValidationProvider實(shí)現(xiàn)堆生。比如HibernateValidator就是一個(gè)ValidationProvider的實(shí)現(xiàn):
HibernateValidatorConfiguration configuration = Validation.byProvider(HibernateValidator.class)
        // .providerResolver( ... ) // 因?yàn)橹贫薖rovider,這個(gè)參數(shù)就可選了
        .configure()
        .failFast(false);
ValidatorFactory validatorFactory = configuration.buildValidatorFactory();

這三種初始化方式雷酪,在源碼處就是對(duì)應(yīng)提供的三個(gè)public static方法:

public class Validation {

    // 方式一
    public static ValidatorFactory buildDefaultValidatorFactory() {
        return byDefaultProvider().configure().buildValidatorFactory();
    }
    // 方式二
    public static GenericBootstrap byDefaultProvider() {
        return new GenericBootstrapImpl();
    }
    // 方式三
    public static <T extends Configuration<T>, U extends ValidationProvider<T>> ProviderSpecificBootstrap<T> byProvider(Class<U> providerType) {
        return new ProviderSpecificBootstrapImpl<>( providerType );
    }
    ...
}

對(duì)于若你想使用xml文件獨(dú)立配置校驗(yàn)規(guī)則淑仆,可以使用Configuration.addMapping(new FileInputStream(validationFile));,現(xiàn)在很少這么使用哥力,略~
使用注意事項(xiàng):ValidatorFactory被創(chuàng)建后應(yīng)該緩存起來(lái)再提供使用蔗怠,因?yàn)樗强h城安全的。

因?yàn)楝F(xiàn)在都會(huì)使用Hibernate-Validation來(lái)處理校驗(yàn)吩跋,因此此處只關(guān)心方式三~

HibernateValidatorConfiguration

此接口表示配置寞射,繼承自標(biāo)注接口javax.validation.Configuration。很明顯锌钮,它是HibernateValidator的專屬配置類


先看頂級(jí)接口:javax.validation.Configuration怠惶,為構(gòu)建ValidatorFactory的配置類。默認(rèn)情況下轧粟,它會(huì)讀取配置文件META-INF/validation.xml,Configuration提供的API方法是覆蓋xml配置文件項(xiàng)的脓魏。若沒(méi)有找到validation.xml兰吟,就會(huì)使用默認(rèn)的ValidationProviderResolver也就是:DefaultValidationProviderResolver

public interface Configuration<T extends Configuration<T>> {
    // 該方法調(diào)用后就不會(huì)再去找META-INF/validation.xml了
    T ignoreXmlConfiguration();
    // 消息內(nèi)插器  它是個(gè)狠角色茂翔,關(guān)于它的使用場(chǎng)景混蔼,后續(xù)會(huì)有詳解(包括Spring都實(shí)現(xiàn)了它來(lái)做事)
    // 它的作用是:插入給定的約束沖突消息
    T messageInterpolator(MessageInterpolator interpolator);
    // 確定bean驗(yàn)證提供程序是否可以訪問(wèn)屬性的協(xié)定。對(duì)每個(gè)正在驗(yàn)證或級(jí)聯(lián)的屬性調(diào)用此約定珊燎。(Spring木有實(shí)現(xiàn)它)
    // 對(duì)每個(gè)正在驗(yàn)證或級(jí)聯(lián)的屬性都會(huì)調(diào)用此約定
    // Traversable: 可移動(dòng)的
    T traversableResolver(TraversableResolver resolver);
    // 創(chuàng)建ConstraintValidator的工廠
    // ConstraintValidator:定義邏輯以驗(yàn)證給定對(duì)象類型T的給定約束A惭嚣。(A是個(gè)注解類型)
    T constraintValidatorFactory(ConstraintValidatorFactory constraintValidatorFactory);
    // ParameterNameProvider:提供Constructor/Method的方法名們
    T parameterNameProvider(ParameterNameProvider parameterNameProvider);
    // java.time.Clock 用作判定@Future和@Past(默認(rèn)取值當(dāng)前時(shí)間)
    // 若你希望他是個(gè)邏輯實(shí)現(xiàn)遵湖,提供一個(gè)它即可
    // @since 2.0
    T clockProvider(ClockProvider clockProvider);
    // 值提取器。這是add哦~ 負(fù)責(zé)從Optional晚吞、List等這種容器里提取值~
    // @since 2.0
    T addValueExtractor(ValueExtractor<?> extractor);
    // 加載xml文件
    T addMapping(InputStream stream);
    // 添加特定的屬性給Provider用的延旧。此屬性等效于XML配置屬性。
    // 此方法通常是框架自己分析xml文件得到屬性值然后放進(jìn)去槽地,調(diào)用者一般不使用(當(dāng)然也可以用)
    T addProperty(String name, String value);
    
    // 下面都是get方法嘍
    MessageInterpolator getDefaultMessageInterpolator();
    TraversableResolver getDefaultTraversableResolver();
    ConstraintValidatorFactory getDefaultConstraintValidatorFactory();
    ParameterNameProvider getDefaultParameterNameProvider();
    ClockProvider getDefaultClockProvider();
    BootstrapConfiguration getBootstrapConfiguration(); // 整個(gè)配置也可返回出去

    // 上面都是工作迁沫,這個(gè)方法才是最終需要調(diào)用的:得到一個(gè)ValidatorFactory
    ValidatorFactory buildValidatorFactory();
}

該接口提供了一些標(biāo)準(zhǔn)的配置項(xiàng)。在實(shí)際應(yīng)用中都是使用Hibernate Validation捌蚊,所以再看看這個(gè)具體的子接口:

public interface HibernateValidatorConfiguration extends Configuration<HibernateValidatorConfiguration> {

    // 這批屬性集畅,證明直接可以通過(guò)System屬性值來(lái)控制,大大地方便~
    // 這個(gè)機(jī)制快速失敗機(jī)制:true檢查完一個(gè)有錯(cuò)誤就返回缅糟,false全部檢查完把錯(cuò)誤消息一起返回   默認(rèn)false
    String FAIL_FAST = "hibernate.validator.fail_fast"; 
    String ALLOW_PARAMETER_CONSTRAINT_OVERRIDE = "hibernate.validator.allow_parameter_constraint_override";
    String ALLOW_MULTIPLE_CASCADED_VALIDATION_ON_RESULT = "hibernate.validator.allow_multiple_cascaded_validation_on_result";
    String ALLOW_PARALLEL_METHODS_DEFINE_PARAMETER_CONSTRAINTS = "hibernate.validator.allow_parallel_method_parameter_constraint";
    // @since 5.2
    @Deprecated
    String CONSTRAINT_MAPPING_CONTRIBUTOR = "hibernate.validator.constraint_mapping_contributor";
    // @since 5.3
    String CONSTRAINT_MAPPING_CONTRIBUTORS = "hibernate.validator.constraint_mapping_contributors";
    // @since 6.0.3
    String ENABLE_TRAVERSABLE_RESOLVER_RESULT_CACHE = "hibernate.validator.enable_traversable_resolver_result_cache";
    // @since 6.0.3  ScriptEvaluatorFactory:執(zhí)行腳本
    @Incubating
    String SCRIPT_EVALUATOR_FACTORY_CLASSNAME = "hibernate.validator.script_evaluator_factory";
    // @since 6.0.5 comparing date/time in temporal constraints. In milliseconds.
    @Incubating
    String TEMPORAL_VALIDATION_TOLERANCE = "hibernate.validator.temporal_validation_tolerance";

    // ResourceBundleMessageInterpolator用于 load resource bundles
    ResourceBundleLocator getDefaultResourceBundleLocator();
    // 創(chuàng)建一個(gè)ConstraintMapping:通過(guò)編程API配置的約束映射
    // 設(shè)置映射后挺智,必須通過(guò)addMapping(constraintmapping)將其添加到此配置中。
    ConstraintMapping createConstraintMapping();
    // 拿到所有的值提取器  @since 6.0
    @Incubating
    Set<ValueExtractor<?>> getDefaultValueExtractors();

    // 往下就開(kāi)始配置了~~~~~~~~~~
    HibernateValidatorConfiguration addMapping(ConstraintMapping mapping);
    HibernateValidatorConfiguration failFast(boolean failFast);
    // used for loading user-provided resources:
    HibernateValidatorConfiguration externalClassLoader(ClassLoader externalClassLoader);
    // true:表示允許覆蓋約束的方法窗宦。false表示不予許(拋出異常)  默認(rèn)值是false
    HibernateValidatorConfiguration allowOverridingMethodAlterParameterConstraint(boolean allow);
    // 定義是否允許對(duì)返回值標(biāo)記多個(gè)約束以進(jìn)行級(jí)聯(lián)驗(yàn)證赦颇。  默認(rèn)是false
    HibernateValidatorConfiguration allowMultipleCascadedValidationOnReturnValues(boolean allow);
    // 定義約束的**并行方法**是否應(yīng)引發(fā)ConstraintDefinitionException
    HibernateValidatorConfiguration allowParallelMethodsDefineParameterConstraints(boolean allow);
    // 是否允許緩存TraversableResolver  默認(rèn)值是true
    HibernateValidatorConfiguration enableTraversableResolverResultCache(boolean enabled);
    // 設(shè)置一個(gè)腳本執(zhí)行器
    @Incubating
    HibernateValidatorConfiguration scriptEvaluatorFactory(ScriptEvaluatorFactory scriptEvaluatorFactory);
    // 允許在時(shí)間約束中比較日期/時(shí)間時(shí)設(shè)置可接受的誤差范圍
    // 比如@Past @PastOrPresent @Future @FutureOrPresent
    @Incubating
    HibernateValidatorConfiguration temporalValidationTolerance(Duration temporalValidationTolerance);
    // 允許設(shè)置將傳遞給約束驗(yàn)證器的有效負(fù)載。如果多次調(diào)用該方法迫摔,則只傳播最后傳遞的有效負(fù)載沐扳。
    @Incubating
    HibernateValidatorConfiguration constraintValidatorPayload(Object constraintValidatorPayload);
}

關(guān)于此接口的唯一實(shí)現(xiàn)類:ConfigurationImpl,這里就不用再做分析了句占,因?yàn)閷?duì)于Validation這塊沪摄,咱們面向接口編程是完全沒(méi)有問(wèn)題的~

準(zhǔn)備好了Configuration后,下一步顯然就是configuration.buildValidatorFactory()來(lái)得到一個(gè)ValidatorFactory嘍纱烘,關(guān)于ValidatorFactory這塊的內(nèi)容杨拐,請(qǐng)聽(tīng)下文分解~

總結(jié)

該文講解是關(guān)于Bean Validation數(shù)據(jù)校驗(yàn),在現(xiàn)在Spring的高度封裝下擂啥,越來(lái)越少的人能夠主動(dòng)去發(fā)現(xiàn)Java實(shí)現(xiàn)/標(biāo)準(zhǔn)了~
實(shí)際上Spring的強(qiáng)大并不是自己創(chuàng)造了多少輪子哄陶,而是它主要是帶來(lái)了更為簡(jiǎn)單的抽象,從而減少樣板代碼哺壶、促進(jìn)解耦屋吨、提高可單測(cè)性。因此對(duì)于有些常用的功能還是建議稍微了解多一點(diǎn)山宾,做到心中有數(shù)至扰,運(yùn)用起來(lái)也才會(huì)更加的游刃有余

知識(shí)交流

若文章格式混亂,可點(diǎn)擊原文鏈接-原文鏈接-原文鏈接-原文鏈接-原文鏈接

==The last:如果覺(jué)得本文對(duì)你有幫助资锰,不妨點(diǎn)個(gè)贊唄敢课。當(dāng)然分享到你的朋友圈讓更多小伙伴看到也是被作者本人許可的~==

若對(duì)技術(shù)內(nèi)容感興趣可以加入wx群交流:Java高工、架構(gòu)師3群
若群二維碼失效直秆,請(qǐng)加wx號(hào):fsx641385712(或者掃描下方wx二維碼)濒募。并且備注:"java入群" 字樣,會(huì)手動(dòng)邀請(qǐng)入群

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末圾结,一起剝皮案震驚了整個(gè)濱河市瑰剃,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌疫稿,老刑警劉巖培他,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異遗座,居然都是意外死亡舀凛,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門途蒋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)猛遍,“玉大人,你說(shuō)我怎么就攤上這事号坡“每荆” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵宽堆,是天一觀的道長(zhǎng)腌紧。 經(jīng)常有香客問(wèn)我,道長(zhǎng)畜隶,這世上最難降的妖魔是什么壁肋? 我笑而不...
    開(kāi)封第一講書人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮籽慢,結(jié)果婚禮上浸遗,老公的妹妹穿的比我還像新娘。我一直安慰自己箱亿,他們只是感情好跛锌,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著届惋,像睡著了一般髓帽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上脑豹,一...
    開(kāi)封第一講書人閱讀 51,708評(píng)論 1 305
  • 那天郑藏,我揣著相機(jī)與錄音,去河邊找鬼晨缴。 笑死,一個(gè)胖子當(dāng)著我的面吹牛峡捡,可吹牛的內(nèi)容都是我干的击碗。 我是一名探鬼主播筑悴,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼稍途!你這毒婦竟也來(lái)了阁吝?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤械拍,失蹤者是張志新(化名)和其女友劉穎突勇,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體坷虑,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡甲馋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了迄损。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片定躏。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖芹敌,靈堂內(nèi)的尸體忽然破棺而出痊远,到底是詐尸還是另有隱情,我是刑警寧澤氏捞,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布碧聪,位于F島的核電站,受9級(jí)特大地震影響液茎,放射性物質(zhì)發(fā)生泄漏逞姿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一豁护、第九天 我趴在偏房一處隱蔽的房頂上張望哼凯。 院中可真熱鬧,春花似錦楚里、人聲如沸断部。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蝴光。三九已至,卻和暖如春达址,著一層夾襖步出監(jiān)牢的瞬間蔑祟,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工沉唠, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留疆虚,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像径簿,于是被迫代替她去往敵國(guó)和親罢屈。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容