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。
- Bean Validation 中內(nèi)置的 constraint
- Hibernate Validator 附加的 constraint (hibernate補(bǔ)充的注解中克饶,最后3個不常用酝蜒,可忽略)
@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引入依賴
第二步:JavaBean 添加校驗注解
第三步:@Valid參數(shù)校驗
注意事項:使用@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 方法.......
}