一、前言
在后台开发过程中,对参数的校验成为开发环境不可缺少的一个环节。比如参数不能为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上维护,最新包的坐标如下:
<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