Java Bean Validation

Java Bean Validation

參數(shù)校驗是我們程序開發(fā)中必不可少的過程旬渠。用戶在前端頁面上填寫表單時迫吐,前端js程序會校驗參數(shù)的合法性讯检,當(dāng)數(shù)據(jù)到了后端姆涩,為了防止惡意操作挽拂,保持程序的健壯性,后端同樣需要對數(shù)據(jù)進(jìn)行校驗骨饿。后端參數(shù)校驗最簡單的做法是直接在業(yè)務(wù)方法里面進(jìn)行判斷亏栈,當(dāng)判斷成功之后再繼續(xù)往下執(zhí)行。但這樣帶給我們的是代碼的耦合宏赘,冗余绒北。當(dāng)我們多個地方需要校驗時,我們就需要在每一個地方調(diào)用校驗程序,導(dǎo)致代碼很冗余察署,且不美觀闷游。

那么如何優(yōu)雅的對參數(shù)進(jìn)行校驗?zāi)兀縅SR303就是為了解決這個問題出現(xiàn)的贴汪,本篇文章主要是介紹 JSR303储藐,Hibernate Validator 等校驗工具的使用,以及自定義校驗注解的使用嘶是。

校驗框架介紹

JSR303 是一套JavaBean參數(shù)校驗的標(biāo)準(zhǔn)钙勃,它定義了很多常用的校驗注解,我們可以直接將這些注解加在我們JavaBean的屬性上面聂喇,就可以在需要校驗的時候進(jìn)行校驗了辖源。注解如下:

Bean Validation是一個通過配置注解來驗證參數(shù)的框架蔚携,它包含兩部分Bean Validation API和Hibernate Validator。

  1. Bean Validation 中內(nèi)置的 constraint
image
  1. Hibernate Validator 附加的 constraint (hibernate補(bǔ)充的注解中克饶,最后3個不常用酝蜒,可忽略)
image

@NotNull @NotEmpty @NotBlank 3個注解的區(qū)別:

@NotNull 任何對象的value不能為null

@NotEmpty 集合對象的元素不為0,即集合不為空矾湃,也可以用于字符串不為null

@NotBlank 只能用于字符串不為null亡脑,并且字符串trim()以后length要大于0

Spring validtor 同樣擴(kuò)展了jsr303,并實(shí)現(xiàn)了方法參數(shù)和返回值的校驗

Spring 提供了MethodValidationPostProcessor類,用于對方法的校驗

第一步:pom.xml引入依賴

image

第二步:JavaBean 添加校驗注解

image

第三步:@Valid參數(shù)校驗

image

注意事項:使用@Valid 參數(shù)校驗時邀跃,如果參數(shù)校驗不過霉咨,則回拋出400 異常

擴(kuò)展知識:自定義校驗注解

雖然jSR303和Hibernate Validtor 提供了很多注解,但在實(shí)際使用中拍屑,往往不能滿足使用需求途戒,需要進(jìn)行組合,更復(fù)雜的校驗方式僵驰。

1.分組驗證:

同一javabean喷斋, 但根據(jù)不同的業(yè)務(wù)邏輯進(jìn)行參數(shù)校驗,例如新增or修改

1.1 定義兩個空接口

/**
 * 可以在一個Model上面添加多套參數(shù)驗證規(guī)則蒜茴,此接口定義添加Person模型新增時的參數(shù)校驗規(guī)則
 */
public interface PersonAddView {
}

/**
 * 可以在一個Model上面添加多套參數(shù)驗證規(guī)則星爪,此接口定義添加Person模型修改時的參數(shù)校驗規(guī)則
 */
public interface PersonModifyView {
}

1.2 添加注解時使用指明所述的分組

public class Person {
    private long id;
    /**
     * 添加groups 屬性,說明只在特定的驗證規(guī)則里面起作用粉私,不加則表示在使用Deafault規(guī)則時起作用
     */
    @NotNull(groups = {PersonAddView.class, PersonModifyView.class}, message = "添加顽腾、修改用戶時名字不能為空", payload = ValidateErrorLevel.Info.class)
    @ListNotHasNull.List({
            @ListNotHasNull(groups = {PersonAddView.class}, message = "添加上Name不能為空"),
            @ListNotHasNull(groups = {PersonModifyView.class}, message = "修改時Name不能為空")})
    private String name;

    @NotNull(groups = {PersonAddView.class}, message = "添加用戶時地址不能為空")
    private String address;

    @Min(value = 18, groups = {PersonAddView.class}, message = "姓名不能低于18歲")
    @Max(value = 30, groups = {PersonModifyView.class}, message = "姓名不能超過30歲")
    private int age;
  //getter setter 方法......
}

1.3 啟用校驗(指定組)

/**
     * 添加一個Person對象
     * 此處啟用PersonAddView 這個驗證規(guī)則
     * 備注:此處@Validated(PersonAddView.class) 表示使用PersonAndView這套校驗規(guī)則,若使用@Valid 則表示使用默認(rèn)校驗規(guī)則毡鉴,
     * 若兩個規(guī)則同時加上去崔泵,則只有第一套起作用
     */
    @RequestMapping(value = "/person", method = RequestMethod.POST)
    public void addPerson(@RequestBody @Validated({PersonAddView.class, Default.class}) Person person) {
        System.out.println(person.toString());
    }

    /**
     * 修改Person對象
     * 此處啟用PersonModifyView 這個驗證規(guī)則
     */
    @RequestMapping(value = "/person", method = RequestMethod.PUT)
    public void modifyPerson(@RequestBody @Validated(value = {PersonModifyView.class}) Person person) {
        System.out.println(person.toString());
    }

2.List參數(shù)校驗:

2.1 定義注解

package com.test.validate.annotation;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * 自定義參數(shù)校驗注解
 * 校驗 List 集合中是否有null 元素
 */

@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = ListNotHasNullValidatorImpl.class)//此處指定了注解的實(shí)現(xiàn)類為ListNotHasNullValidatorImpl

public @interface ListNotHasNull {

    /**
     * 添加value屬性,可以作為校驗時的條件,若不需要猪瞬,可去掉此處定義
     */
    int value() default 0;

    String message() default "List集合中不能含有null元素";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    /**
     * 定義List憎瘸,為了讓Bean的一個屬性上可以添加多套規(guī)則
     */
    @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
    @Retention(RUNTIME)
    @Documented
    @interface List {
        ListNotHasNull[] value();
    }
}

2.2 實(shí)現(xiàn)注解方法

package com.test.validate.annotation;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * 自定義參數(shù)校驗注解
 * 校驗 List 集合中是否有null 元素
 */
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = ListNotHasNullValidatorImpl.class)////此處指定了注解的實(shí)現(xiàn)類為ListNotHasNullValidatorImpl

public @interface ListNotHasNull {

    /**
     * 添加value屬性,可以作為校驗時的條件,若不需要陈瘦,可去掉此處定義
     */
    int value() default 0;

    String message() default "List集合中不能含有null元素";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    /**
     * 定義List幌甘,為了讓Bean的一個屬性上可以添加多套規(guī)則
     */
    @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
    @Retention(RUNTIME)
    @Documented
    @interface List {
        ListNotHasNull[] value();
    }
}

2.3 使用注解

public class User {

    //其他參數(shù) .......

    /**
     * 所擁有的書籍列表
     */
    @NotEmpty(message = "所擁有書籍不能為空")
    @ListNotHasNull(message = "List 中不能含有null元素")
    @Valid
    private List<Book> books;
    //getter setter 方法.......
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市痊项,隨后出現(xiàn)的幾起案子锅风,更是在濱河造成了極大的恐慌,老刑警劉巖鞍泉,帶你破解...
    沈念sama閱讀 216,997評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件皱埠,死亡現(xiàn)場離奇詭異,居然都是意外死亡咖驮,警方通過查閱死者的電腦和手機(jī)边器,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評論 3 392
  • 文/潘曉璐 我一進(jìn)店門训枢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人忘巧,你說我怎么就攤上這事恒界。” “怎么了砚嘴?”我有些...
    開封第一講書人閱讀 163,359評論 0 353
  • 文/不壞的土叔 我叫張陵十酣,是天一觀的道長。 經(jīng)常有香客問我际长,道長耸采,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,309評論 1 292
  • 正文 為了忘掉前任也颤,我火速辦了婚禮洋幻,結(jié)果婚禮上郁轻,老公的妹妹穿的比我還像新娘翅娶。我一直安慰自己,他們只是感情好好唯,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,346評論 6 390
  • 文/花漫 我一把揭開白布竭沫。 她就那樣靜靜地躺著,像睡著了一般骑篙。 火紅的嫁衣襯著肌膚如雪蜕提。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,258評論 1 300
  • 那天靶端,我揣著相機(jī)與錄音谎势,去河邊找鬼。 笑死杨名,一個胖子當(dāng)著我的面吹牛脏榆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播台谍,決...
    沈念sama閱讀 40,122評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼须喂,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了趁蕊?” 一聲冷哼從身側(cè)響起坞生,我...
    開封第一講書人閱讀 38,970評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎掷伙,沒想到半個月后是己,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,403評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡任柜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,596評論 3 334
  • 正文 我和宋清朗相戀三年卒废,在試婚紗的時候發(fā)現(xiàn)自己被綠了寒波。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,769評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡升熊,死狀恐怖俄烁,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情级野,我是刑警寧澤页屠,帶...
    沈念sama閱讀 35,464評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站蓖柔,受9級特大地震影響辰企,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜况鸣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,075評論 3 327
  • 文/蒙蒙 一牢贸、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧镐捧,春花似錦潜索、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至列牺,卻和暖如春整陌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瞎领。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評論 1 269
  • 我被黑心中介騙來泰國打工泌辫, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人九默。 一個月前我還...
    沈念sama閱讀 47,831評論 2 370
  • 正文 我出身青樓震放,卻偏偏與公主長得像,于是被迫代替她去往敵國和親荤西。 傳聞我的和親對象是個殘疾皇子澜搅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,678評論 2 354

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