一、前言
在后台开发过程中,对参数的校验成为开发环境不可缺少的一个环节。比如参数不能为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上维护,最新包的坐标如下:
下载之后打开这个包,有个package叫constraints,里面放的就是验证的的注解:
下面开始用代码实践一下:
1、定义一个待验证的bean:Student.java
1 packagecom.shishang;2
3 import javax.validation.constraints.*;4 importjava.io.Serializable;5 importjava.math.BigDecimal;6 importjava.util.Date;7
8 public class Student implementsSerializable {9
10
11 @NotNull(message = "名字不能为空")12 privateString name;13
14 @Size(min = 6,max = 30,message = "地址应该在6-30字符之间")15 privateString address;16
17 @DecimalMax(value = "100.00",message = "体重有些超标哦")18 @DecimalMin(value = "60.00",message = "多吃点饭吧")19 privateBigDecimal weight;20
21 privateString friendName;22 @AssertTrue23 privateBoolean isHaveFriend(){24 return friendName != null?true:false;25 }26
27 @Future(message = "生日必须在当前实践之前")28 privateDate birthday;29
30 @Pattern(regexp = "^(.+)@(.+)$",message = "邮箱的格式不合法")31 privateString email;32
33
34 publicString getName() {35 returnname;36 }37
38 public voidsetName(String name) {39 this.name =name;40 }41
42 publicString getAddress() {43 returnaddress;44 }45
46 public voidsetAddress(String address) {47 this.address =address;48 }49
50 publicBigDecimal getWeight() {51 returnweight;52 }53
54 public voidsetWeight(BigDecimal weight) {55 this.weight =weight;56 }57
58 publicString getFriendName() {59 returnfriendName;60 }61
62 public voidsetFriendName(String friendName) {63 this.friendName =friendName;64 }65
66 publicDate getBirthday() {67 returnbirthday;68 }69
70 public voidsetBirthday(Date birthday) {71 this.birthday =birthday;72 }73
74 publicString getEmail() {75 returnemail;76 }77
78 public voidsetEmail(String email) {79 this.email =email;80 }81 }
View Code
2、测试类:StudentTest.java
1 packagecom.use;2
3 importjavax.validation.ConstraintViolation;4 importjavax.validation.Validation;5 importjavax.validation.Validator;6 importjavax.validation.ValidatorFactory;7 importjava.io.Serializable;8 importjava.math.BigDecimal;9 importjava.util.ArrayList;10 importjava.util.Date;11 importjava.util.List;12 importjava.util.Set;13
14 public class StudentTest implementsSerializable {15 public static voidmain(String[] args) {16 Student xiaoming =getBean();17 List validate =validate(xiaoming);18 validate.forEach(row ->{19 System.out.println(row.toString());20 });21
22 }23
24 private staticStudent getBean() {25 Student bean = newStudent();26 bean.setName(null);27 bean.setAddress("北京");28 bean.setBirthday(newDate());29 bean.setFriendName(null);30 bean.setWeight(new BigDecimal(30));31 bean.setEmail("xiaogangfan163.com");32 returnbean;33 }34
35 private static ValidatorFactory factory =Validation.buildDefaultValidatorFactory();36
37 public static Listvalidate(T t) {38 Validator validator =factory.getValidator();39 Set> constraintViolations =validator.validate(t);40
41 List messageList = new ArrayList<>();42 for (ConstraintViolationconstraintViolation : constraintViolations) {43 messageList.add(constraintViolation.getMessage());44 }45 returnmessageList;46 }47 }
View Code
3、运行testValidation()方法,输处如下:
地址应该在6-30字符之间
邮箱的格式不合法
生日必须在当前时间之前
多吃点饭吧
名字不能为空
4、总结
像@NotNull、@Size等比较简单也易于理解,不多说
因为bean validation只提供了接口并未实现,使用时需要加上一个provider的包,例如hibernate-validator
@Pattern 因为这个是正则,所以能做的事情比较多,比如中文还是数字、邮箱、长度等等都可以做
@AssertTRue 这个与其他的校验注解有着本质的区别,这个注解适用于多个字段。例子中isHaveFriend方法依赖friendName字段校验
验证的api是经过我加工了一下,这样可以批量返回校验的信息
有时我们需要的注解可能没有提供,这时候就需要自定义注解,写实现类,下面说一下自定义注解的使用
四、自定义bean validation 注解验证
有时框架自带的没法满足我们的需求,这时就需要自己动手丰衣足食了,恩恩 ,这个不难,下面说下。
这个例子验证字符串是大写还是小写约束标注,代码如下:
1、枚举类型CaseMode, 来表示大写或小写模式
1 packagecom.defineconstrain;2
3 /**
4 * created by xiaogangfan5 * on 16/10/25.6 */
7 public enumCaseMode {8 UPPER,9 LOWER;10 }
View Code
2、定义一个CheckCase的约束标注
1 packagecom.defineconstrain;2
3 /**
4 * created by xiaogangfan5 * on 16/10/25.6 */
7 import static java.lang.annotation.ElementType.*;8 import static java.lang.annotation.RetentionPolicy.*;9
10 importjava.lang.annotation.Documented;11 importjava.lang.annotation.Retention;12 importjava.lang.annotation.Target;13
14 importjavax.validation.Constraint;15 importjavax.validation.Payload;16
17 @Target( { METHOD, FIELD, ANNOTATION_TYPE })18 @Retention(RUNTIME)19 @Constraint(validatedBy = CheckCaseValidator.class)20 @Documented21 public @interfaceCheckCase {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 }
View Code
3、约束条件CheckCase的验证器
1 packagecom.defineconstrain;2
3 /**
4 * created by xiaogangfan5 * on 16/10/25.6 */
7 importjavax.validation.ConstraintValidator;8 importjavax.validation.ConstraintValidatorContext;9
10 public class CheckCaseValidator implements ConstraintValidator{11
12 privateCaseMode caseMode;13
14 public voidinitialize(CheckCase constraintAnnotation) {15 this.caseMode =constraintAnnotation.value();16 }17
18 public booleanisValid(String object, ConstraintValidatorContext constraintContext) {19
20 if (object == null)21 return true;22
23 if (caseMode ==CaseMode.UPPER)24 returnobject.equals(object.toUpperCase());25 else
26 returnobject.equals(object.toLowerCase());27 }28
29 }
View Code
4、在Student.java中增加一个属性
1 @CheckCase(value = CaseMode.LOWER,message = "名字的拼音需要小写")2 private String spellName;
View Code
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