一、前言
在后台開發過程中,對參數的校驗成為開發環境不可缺少的一個環節。比如參數不能為null,email那么必須符合email的格式,如果手動進行if判斷或者寫正則表達式判斷無意開發效率太慢,在時間、成本、質量的博弈中必然會落后。所以把校驗層抽象出來是必然的結果,下面說下幾種解決方案。
二、幾種解決方案
1、struts2的valid可以通過配置xml,xml中描述規則和返回的信息,這種方式比較麻煩、開發效率低,不推薦
2、validation bean 是基於JSR-303標准開發出來的,使用注解方式實現,及其方便,但是這只是一個接口,沒有具體實現.Hibernate Validator是一個hibernate獨立的包,可以直接引用,他實現了validation bean同時有做了擴展,比較強大 ,實現圖如下:
3、oval 是一個可擴展的Java對象數據驗證框架,驗證的規則可以通過配置文件、Annotation、POJOs 進行設定。可以使用純 Java 語言、JavaScript 、Groovy 、BeanShell 等進行規則的編寫,本次不過多講解
三、bean validation 框架驗證介紹
bean validation 包放在maven上維護,最新包的坐標如下:
1
2
3
4
5
|
<
dependency
>
<
groupId
>javax.validation</
groupId
>
<
artifactId
>validation-api</
artifactId
>
<
version
>1.1.0.Final</
version
>
</
dependency
>
|
下載之后打開這個包,有個package叫constraints,里面放的就是驗證的的注解:
下面開始用代碼實踐一下:
1、定義一個待驗證的bean:Student.java

1 package com.shishang; 2 3 import javax.validation.constraints.*; 4 import java.io.Serializable; 5 import java.math.BigDecimal; 6 import java.util.Date; 7 8 public class Student implements Serializable { 9 10 11 @NotNull(message = "名字不能為空") 12 private String name; 13 14 @Size(min = 6,max = 30,message = "地址應該在6-30字符之間") 15 private String address; 16 17 @DecimalMax(value = "100.00",message = "體重有些超標哦") 18 @DecimalMin(value = "60.00",message = "多吃點飯吧") 19 private BigDecimal weight; 20 21 private String friendName; 22 @AssertTrue 23 private Boolean isHaveFriend(){ 24 return friendName != null?true:false; 25 } 26 27 @Future(message = "生日必須在當前實踐之前") 28 private Date birthday; 29 30 @Pattern(regexp = "^(.+)@(.+)$",message = "郵箱的格式不合法") 31 private String email; 32 33 34 public String getName() { 35 return name; 36 } 37 38 public void setName(String name) { 39 this.name = name; 40 } 41 42 public String getAddress() { 43 return address; 44 } 45 46 public void setAddress(String address) { 47 this.address = address; 48 } 49 50 public BigDecimal getWeight() { 51 return weight; 52 } 53 54 public void setWeight(BigDecimal weight) { 55 this.weight = weight; 56 } 57 58 public String getFriendName() { 59 return friendName; 60 } 61 62 public void setFriendName(String friendName) { 63 this.friendName = friendName; 64 } 65 66 public Date getBirthday() { 67 return birthday; 68 } 69 70 public void setBirthday(Date birthday) { 71 this.birthday = birthday; 72 } 73 74 public String getEmail() { 75 return email; 76 } 77 78 public void setEmail(String email) { 79 this.email = email; 80 } 81 }
2、測試類:StudentTest.java

1 package com.use; 2
3 import javax.validation.ConstraintViolation; 4 import javax.validation.Validation; 5 import javax.validation.Validator; 6 import javax.validation.ValidatorFactory; 7 import java.io.Serializable; 8 import java.math.BigDecimal; 9 import java.util.ArrayList; 10 import java.util.Date; 11 import java.util.List; 12 import java.util.Set; 13
14 public class StudentTest implements Serializable { 15 public static void main(String[] args) { 16 Student xiaoming = getBean(); 17 List<String> validate = validate(xiaoming); 18 validate.forEach(row -> { 19 System.out.println(row.toString()); 20 }); 21
22 } 23
24 private static Student getBean() { 25 Student bean = new Student(); 26 bean.setName(null); 27 bean.setAddress("北京"); 28 bean.setBirthday(new Date()); 29 bean.setFriendName(null); 30 bean.setWeight(new BigDecimal(30)); 31 bean.setEmail("xiaogangfan163.com"); 32 return bean; 33 } 34
35 private static ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); 36
37 public static <T> List<String> validate(T t) { 38 Validator validator = factory.getValidator(); 39 Set<ConstraintViolation<T>> constraintViolations = validator.validate(t); 40
41 List<String> messageList = new ArrayList<>(); 42 for (ConstraintViolation<T> constraintViolation : constraintViolations) { 43 messageList.add(constraintViolation.getMessage()); 44 } 45 return messageList; 46 } 47 }
3、運行testValidation()方法,輸處如下:
地址應該在6-30字符之間 郵箱的格式不合法 生日必須在當前時間之前 多吃點飯吧 名字不能為空
4、總結
- 像@NotNull、@Size等比較簡單也易於理解,不多說
- 因為bean validation只提供了接口並未實現,使用時需要加上一個provider的包,例如hibernate-validator
- @Pattern 因為這個是正則,所以能做的事情比較多,比如中文還是數字、郵箱、長度等等都可以做
- @AssertTRue 這個與其他的校驗注解有着本質的區別,這個注解適用於多個字段。例子中isHaveFriend方法依賴friendName字段校驗
- 驗證的api是經過我加工了一下,這樣可以批量返回校驗的信息
- 有時我們需要的注解可能沒有提供,這時候就需要自定義注解,寫實現類,下面說一下自定義注解的使用
四、自定義bean validation 注解驗證
有時框架自帶的沒法滿足我們的需求,這時就需要自己動手豐衣足食了,恩恩 ,這個不難,下面說下。
這個例子驗證字符串是大寫還是小寫約束標注,代碼如下:
1、枚舉類型CaseMode
, 來表示大寫或小寫模式

1 package com.defineconstrain; 2 3 /** 4 * created by xiaogangfan 5 * on 16/10/25. 6 */ 7 public enum CaseMode { 8 UPPER, 9 LOWER; 10 }
2、定義一個CheckCase的約束標注

1 package com.defineconstrain; 2 3 /** 4 * created by xiaogangfan 5 * on 16/10/25. 6 */ 7 import static java.lang.annotation.ElementType.*; 8 import static java.lang.annotation.RetentionPolicy.*; 9 10 import java.lang.annotation.Documented; 11 import java.lang.annotation.Retention; 12 import java.lang.annotation.Target; 13 14 import javax.validation.Constraint; 15 import javax.validation.Payload; 16 17 @Target( { METHOD, FIELD, ANNOTATION_TYPE }) 18 @Retention(RUNTIME) 19 @Constraint(validatedBy = CheckCaseValidator.class) 20 @Documented 21 public @interface CheckCase { 22 23 String message() default "{com.mycompany.constraints.checkcase}"; 24 25 Class<?>[] groups() default {}; 26 27 Class<? extends Payload>[] payload() default {}; 28 29 CaseMode value(); 30 31 }
3、約束條件CheckCase
的驗證器

1 package com.defineconstrain; 2 3 /** 4 * created by xiaogangfan 5 * on 16/10/25. 6 */ 7 import javax.validation.ConstraintValidator; 8 import javax.validation.ConstraintValidatorContext; 9 10 public class CheckCaseValidator implements ConstraintValidator<CheckCase, String> { 11 12 private CaseMode caseMode; 13 14 public void initialize(CheckCase constraintAnnotation) { 15 this.caseMode = constraintAnnotation.value(); 16 } 17 18 public boolean isValid(String object, ConstraintValidatorContext constraintContext) { 19 20 if (object == null) 21 return true; 22 23 if (caseMode == CaseMode.UPPER) 24 return object.equals(object.toUpperCase()); 25 else 26 return object.equals(object.toLowerCase()); 27 } 28 29 }
4、在Student.java中增加一個屬性

1 @CheckCase(value = CaseMode.LOWER,message = "名字的拼音需要小寫") 2 private String spellName;
5、在StudentTest.java的getBean()方法中增加一行
bean.setSpellName("XIAOGANGFAN");
6、運行testValidation()方法,輸處如下:
地址應該在6-30字符之間
郵箱的格式不合法
生日必須在當前時間之前
多吃點飯吧
名字的拼音需要小寫
名字不能為空
7、說明新增的約束生效了,大功告成
代碼下載地址:git@github.com:xiaogangfan/vaidation.git
命令: git clone git@github.com:xiaogangfan/vaidation.git