使用的主要的作用:进行注解式的参数校验——让代码更少、更加专注于业务逻辑
参数验证是一个常见的问题,例如验证用户输入的密码是否为空、邮箱是否合法等。但是无论是前端还是后台,都需对用户输入进行验证,以此来保证系统数据的正确性。对于web来说,有些人可能理所当然的想在前端验证就行了,但这样是非常错误的做法,前台的验证一般是通过JavaScript,js代码是可以被禁用和篡改的,所以相对后台检验而言,安全性会低一些。前端代码对于用户来说是透明的,稍微有点技术的人就可以绕过这个验证,直接提交数据到后台。无论是前端网页提交的接口,还是提供给外部的接口,参数验证随处可见,也是必不可少的。总之,一切用户的输入都是不可信的。
基于这样的常识,在后端的代码开发中,参数校验是一个永远也绕不开的话题。然而编写参数校验代码的过程是一个技术含量不高,及其耗时,繁琐的过程。比如极大多数的参数校验代码就是:判断一下用户名是否已经存在、用户名长度是否满足要求、用户填写的邮箱是否合法。年龄参数是不是一个整数等等。为了减少重复代码的开发,许多有技术积淀的公司,一般都会提供一套或多套特有的参数校验工具类。以此减少代码量。这种做法在项目中非常普遍,有了这些工具类库,程序员在编写业务代码的过程中,工作效率可以大大提升。
然而,即便聪明如上述做法,我们的业务逻辑中依然还是可以见到大量的参数校验逻辑,倘若项目架构的不够合理,这些与参数校验逻辑有关的代码量会更多,真是XXX的裹脚布,又臭又长。大量繁杂的参数校验代码混杂在业务逻辑中,一来降低了代码的可读性;二是让人对业务本身的理解难度加大,很多刚加入公司的人往往是通过读码来理解业务的,公司的一些业务培训一般只能讲个大概,然而在阅读代码的过程中稍有不慎便会被这些“额外”的代码扰乱思路,对于新手,那更是异常噩梦,倘若老员工辞职了,撒手抛给新来的,这样的项目能不能维护下去都是个问题了。
以上参考:http://blog.csdn.net/nmgrd/article/details/57088192
Hibernate Validator
Express validation rules in a standardized way using annotation-based constraints and benefit from transparent integration with a wide variety of frameworks.(标准化方式使用基于注释的约束来表达验证规则,并从与各种框架的透明集成中受益),可以扩展,可自定义约束的类型。Hibernate Validator 5.x is the reference implementation Bean Validation 1.1!。hibernate Validator是 Bean Validation 的参考实现,Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint 的实现,除此之外还有一些附加的 constraint(约束)。为了满足特定的需求,用户还可以自实现更多的constraint以便满足特定的需求。
网址:http://hibernate.org/validator/
参考文档:http://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/#validator-gettingstarted-createproject
实际的场景
在后台的业务逻辑中,对数据值的校验在各层都存在(展示层,业务层,数据访问层等),并且各层校验的规则又不尽相同,在各层中重复的校验逻辑既导致了不必要的资源消耗,还使得逻辑不够单一(每层都夹杂着校验的逻辑),JSR 303 Bean Validation就是在这种背景下产生的一个数据验证的J2EE规范。使用起来非常的简单规范,而且校验的规则的代码都是一样的,都是基于JavaBean的注解的校验。
实际的使用
** 引入需要的maven配置
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.0.Final</version>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>el-api</artifactId>
<version>2.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>2.2.6</version>
</dependency>
* 创建需要校验的类*
package com.common.utils.Hibernatevalidatedemo;
import org.hibernate.validator.constraints.Email;
import javax.validation.constraints.*;
import java.util.Date;
/**
* descrption: 测试类,看看Validate
* authohr: wangji
* date: 2017-08-12 10:36
*/
public class ValidateInfoBean {
@NotNull(message = "姓名不能为空!")
@Min(value = 1, message = "Id只能大于等于1,小于等于10")
@Max(value = 10, message = "Id只能大于等于1,小于等于10")
private Integer id;
@NotNull(message = "姓名不能为空!")
@Size(min = 2, max = 4, message = "姓名长度必须在{min}和{max}之间")
@Pattern(regexp = "[\u4e00-\u9fa5]+", message = "名称只能输入是中文字符")
private String userName;
@NotNull(message = "密码不能为空!")
@Size(min = 6, max = 12, message = "密码长度必须在{min}和{max}之间")
private String passWord;
@NotNull(message = "日期不能为空!")
@Past(message = "你只能输入过去的日期")
private Date birthday;
@NotNull(message = "邮件不能为空!")
@Email(message = "邮件格式不正确")
private String email;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassWord() {
return passWord;
}
public void setPassWord(String passWord) {
this.passWord = passWord;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
其中该类中用到Max,Min,NotNull,Size等都是JSR 303中内置的约束条件(constraint),还有一些是Hibernate扩充的约束,我们自己也是可以扩充这个约束进行使用。下面看看如何验证吧。
开始校验首先获取校验器
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
Validator validator = validatorFactory.getValidator();
创建一个校验类的实例
ValidateInfoBean infoBean = new ValidateInfoBean();
infoBean.setId(-1);
infoBean.setUserName("中国wj");
infoBean.setPassWord("12345");
infoBean.setEmail("testt.com");
infoBean.setBirthday(new Date(2015,3,2));
校验一共有几种校验的方式(返回的都是违反约束的字段的集合)
//特定类实例,然后分组(就是一个校验的标识,确定先后顺序,是否校验等)
<T> Set<ConstraintViolation<T>> validate(T object, Class< ?>... groups);
//特定实例上的某个字段
<T> Set<ConstraintViolation<T>> validateProperty(T object,
String propertyName,
Class< ?>... groups);
//使用某个类上的某个字段进行校验
<T> Set<ConstraintViolation<T>> validateValue(Class<T> beanType,
String propertyName,
Object value,
Class<?>... groups);
输出函数:
public static void pringValidateStr(Set<ConstraintViolation<ValidateInfoBean>> set2) {
for (ConstraintViolation<ValidateInfoBean> constraintViolation : set2){
log.info("错误:" + constraintViolation.getMessage());
log.info("字段:"+constraintViolation.getPropertyPath().toString());
}
}
校验所有的字段
Set<ConstraintViolation<ValidateInfoBean>> set = validator.validate(infoBean);
pringValidateStr(set);
2017-08-12 13:54:50,931 INFO [Version.java:30] : HV000001: Hibernate Validator 5.4.0.Final
2017-08-12 13:54:51,251 INFO [ValidateTest.java:52] : 错误:你只能输入过去的日期
2017-08-12 13:54:51,251 INFO [ValidateTest.java:53] : 字段:birthday
2017-08-12 13:54:51,251 INFO [ValidateTest.java:52] : 错误:Id只能大于等于1,小于等于10
2017-08-12 13:54:51,252 INFO [ValidateTest.java:53] : 字段:id
2017-08-12 13:54:51,252 INFO [ValidateTest.java:52] : 错误:名称只能输入是中文字符
2017-08-12 13:54:51,253 INFO [ValidateTest.java:53] : 字段:userName
2017-08-12 13:54:51,253 INFO [ValidateTest.java:52] : 错误:邮件格式不正确
2017-08-12 13:54:51,253 INFO [ValidateTest.java:53] : 字段:email
2017-08-12 13:54:51,253 INFO [ValidateTest.java:52] : 错误:密码长度必须在6和12之间
2017-08-12 13:54:51,253 INFO [ValidateTest.java:53] : 字段:passWord
特定实例上的某个字段
Set<ConstraintViolation<ValidateInfoBean>> set3 =validator.validateProperty(infoBean,"userName");
pringValidateStr(set3);
2017-08-12 13:59:14,262 INFO [Version.java:30] : HV000001: Hibernate Validator 5.4.0.Final
2017-08-12 13:59:14,556 INFO [ValidateTest.java:52] : 错误:名称只能输入是中文字符
2017-08-12 13:59:14,557 INFO [ValidateTest.java:53] : 字段:userName
使用某个类上的某个字段进行校验
Set<ConstraintViolation<ValidateInfoBean>> set2 =validator.validateValue(ValidateInfoBean.class,"userName","中国lihai");
pringValidateStr(set2);
2017-08-12 14:00:32,533 INFO [Version.java:30] : HV000001: Hibernate Validator 5.4.0.Final
2017-08-12 14:00:33,136 INFO [ValidateTest.java:52] : 错误:名称只能输入是中文字符
2017-08-12 14:00:33,136 INFO [ValidateTest.java:53] : 字段:userName
2017-08-12 14:00:33,137 INFO [ValidateTest.java:52] : 错误:姓名长度必须在2和4之间
2017-08-12 14:00:33,137 INFO [ValidateTest.java:53] : 字段:userName
还有很多这样的约束
@Null 被注释的元素必须为 null
@NotNull 被注释的元素必须不为 null
@AssertTrue 被注释的元素必须为 true
@AssertFalse 被注释的元素必须为 false
@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max, min) 被注释的元素的大小必须在指定的范围内
@Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past 被注释的元素必须是一个过去的日期
@Future 被注释的元素必须是一个将来的日期
@Pattern(value) 被注释的元素必须符合指定的正则表达式
表 2. Hibernate Validator 附加的 constraint
@Email 被注释的元素必须是电子邮箱地址
@Length 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串的必须非空
@Range 被注释的元素必须在合适的范围内
还有校验分组,确定校验哪些,校验的顺序,就是一个简单的标识,可以随便创建一个标识的类的信息。这里创建了两个空的接口。除此之外还有很多的使用,比如继承,级联,详情见官网,这里仅仅简单的查看如何使用。
package com.common.utils.Hibernatevalidatedemo;
import lombok.extern.slf4j.Slf4j;
import javax.validation.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Past;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import java.util.Date;
import java.util.Set;
/**
* descrption: 校验分组
* authohr: wangji
* date: 2017-08-12 14:04
*/
@GroupSequence({First.class,Second.class,ValidateGroup.class})//类界别校验的顺序
@Slf4j
public class ValidateGroup {
@NotNull(message = "姓名不能为空!", groups = {First.class})
@Size(min = 2, max = 4, message = "姓名长度必须在{min}和{max}之间",groups = {First.class})
@Pattern(regexp = "[\u4e00-\u9fa5]+", message = "名称只能输入是中文字符",groups = {First.class})
private String userName;
@NotNull(message = "密码不能为空!",groups = {Second.class})
@Size(min = 6, max = 12, message = "密码长度必须在{min}和{max}之间",groups = {Second.class})
private String passWord;
@NotNull(message = "日期不能为空!")
@Past(message = "你只能输入过去的日期")
private Date birthday;
public static void main(String[] args) {
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
Validator validator = validatorFactory.getValidator();
ValidateGroup testFirst = new ValidateGroup();
testFirst.setUserName("wangji");
//下面这里使用来分类进行校验,通过Group唯一标识进行区别,也可以使用多个进行先后顺序的校验
//还可以进行类界别的校验顺序@GroupSequence,如果没有定义Group那么就会使用,当前类进行标识
// pringValidateStr(validator.validate(testFirst,First.class));
// 2017-08-12 14:22:13,899 INFO [ValidateGroup.java:44] : 错误:姓名长度必须在2和4之间
// 2017-08-12 14:22:13,900 INFO [ValidateGroup.java:45] : 字段:userName
// 2017-08-12 14:22:13,900 INFO [ValidateGroup.java:44] : 错误:名称只能输入是中文字符
// 2017-08-12 14:22:13,900 INFO [ValidateGroup.java:45] : 字段:userName
// 通过@GroupSequence指定验证顺序:
// 先验证First分组,如果有错误立即返回
// 而不会验证Second分组,接着如果First分组验证通过了,
// 那么才去验证Second分组,最后指定User.class表示那些没有分组的在最后。
// 这样我们就可以实现按顺序验证分组了。
pringValidateStr(validator.validate(testFirst));
// 输出的只是第一个校验错误,其他的不继续校验了
// 2017-08-12 14:27:46,378 INFO [Version.java:30] : HV000001: Hibernate Validator 5.4.0.Final
// 2017-08-12 14:27:46,649 INFO [ValidateGroup.java:55] : 错误:姓名长度必须在2和4之间
// 2017-08-12 14:27:46,650 INFO [ValidateGroup.java:56] : 字段:userName
// 2017-08-12 14:27:46,650 INFO [ValidateGroup.java:55] : 错误:名称只能输入是中文字符
// 2017-08-12 14:27:46,650 INFO [ValidateGroup.java:56] : 字段:userName
}
public static void pringValidateStr(Set<ConstraintViolation<ValidateGroup>> set2) {
for (ConstraintViolation<ValidateGroup> constraintViolation : set2){
log.info("错误:" + constraintViolation.getMessage());
log.info("字段:"+constraintViolation.getPropertyPath().toString());
}
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassWord() {
return passWord;
}
public void setPassWord(String passWord) {
this.passWord = passWord;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
以上就是简单的使用,还有很多的新特性,可以参考官方上的说明进行简单的查看。