Bean Validation 是一个java规范。可以通过注解的方式约束定义的对象模型或约束方法的入参和出参对象
Bean Validation 1.0(JSR303)是最早的一版java标准对象验证规范,是Java EE 6的一部分。认证的具体实现有:
名称 | 版本 |
---|---|
Hibernate Validator | 4.3.1.Final |
Apache BVal | 0.5 |
Bean Validation 1.1(JSR349)是Java EE 7的一部分。
认证的具体实现有:
名称 | 版本 |
---|---|
Hibernate Validator | 4.3.1.Final |
Apache BVal | 1.1.2 |
较JSR303新增
Bean Validation 2.0(JSR380)是Java EE 8的一部分。
认证的具体实现有:
名称 | 版本 |
---|---|
Hibernate Validator | 6.0.1.Final |
较JSR380新增
为什么要使用Bean Validation ?
验证参数 BEFORE
Book book = new Book();
if (book != null
&& book.getCategory() != null
&& book.getCategory().trim().length() != 0
&& book.getTitle() != null
&& book.getTitle().trim().length() != 0
&& book.getWriter() != null
&& book.getWriter().trim().length() != 0) {
// TODO
}
验证参数 AFTER
public class Book {
@NotBlank
private String title;
@NotBlank
private String writer;
@NotBlank
private String category;
// setter/getter
}
validator.validate(new Book())
Bean Validation 怎么用?
MAVEN依赖:
org.hibernate
hibernate-validator
6.0.1.Final
org.glassfish
javax.el
3.0.1-b06
常用的注解约束解释
public class BeanForValidate {
/**
* 元素: CharSequence
* 约束:不能为null, 至少包含一个非空字符
*/
@NotBlank
private String blankStr = " ";
/**
* 元素:CharSequence(length)、Collection(size)、Map(size)、Array(length)
* 约束:不能为null, 不能为空
*/
@NotEmpty
private String emptyStr = " ";
@NotEmpty
private List emptyList;
/**
* 元素:BigDecimal、BigInteger、byte、short、int、long(包括原型的包装类)
* 约束:min <= element <=max , null 合法
*/
@Min(value = 11)
@Max(value = 21)
// 11<=size<=21
private Integer minMaxInt = 10;
@Min(value = 21)
private BigDecimal minDecimal = new BigDecimal("20.9");
/**
* 元素:BigDecimal、BigInteger、CharSequence、byte、short、int、long(包括原型的包装类)
* 约束:element >= DecimalMin, null 合法
*/
@DecimalMin(value = "100.2")
private String decimalMinDecimal = "100.1";
/**
* 元素:CharSequence(length)、Collection(size)、Map(size)、Array(length)
* 约束:size(min) <= element >= size(max),null 合法
*/
@Size(min = 1, max = 3)
// 1<=size<=3
private String sizeStr = "size";
@Size(min = 1)
@NotNull
private List nullSizeList = new ArrayList<>();
{
nullSizeList.add("A");
nullSizeList.add("B");
}
/**
* 元素:BigDecimal、BigInteger、byte、short、int、long、float、double(包括原型的包装类)
* 约束:必须是正数, 0非法 ,null 合法
*/
@Positive
private Integer positive = -1;
/**
* 元素:BigDecimal、BigInteger、CharSequence、byte、short、int、long(包括原型的包装类)
* 约束:element整数位数=integer , element小数位数=fraction ,null 合法
*/
@Digits(integer = 3, fraction = 2)
// 只允许在3位整数和2位小数范围内
private String digits = "99.212";
/**
* 元素:Date、Calendar、Instant、LocalDate、LocalDateTime、LocalTime、MonthDay、OffsetDateTime、
* OffsetTime、Year、YearMonth、ZonedDateTime、HijrahDate、JapaneseDate、MinguoDate、ThaiBuddhistDate
* 约束:时间是在未来,当前时间默认的是 JVM的时间,null 合法
*/
@Future
private Date passTime;
{
Calendar instance = Calendar.getInstance();
instance.add(Calendar.DAY_OF_YEAR, -1);
passTime = instance.getTime();
}
/**
* 元素:CharSequence
* 约束:是否符合正则表达式,null 合法
*/
@Pattern(message = "身份证账号格式错误", regexp = "(^[1-9]\\d{5}(18|19|([23]\\d))\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$)|(^[1-9]\\d{5}\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}$)/")
// 身份证号验证
private String idCard = "92002199902137720";
@Email
@NotBlank
private String mail = "[email protected]";
}
校验:
BeanForValidate beanForValidate = new BeanForValidate();
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Set> result = validator.validate(beanForValidate);
List message
= result.stream().map(v -> v.getPropertyPath() + " " + v.getMessage() + ": " + v.getInvalidValue())
.collect(Collectors.toList());
结果:
positive must be greater than 0: -1
minMaxInt 最小不能小于11: 10
minDecimal 最小不能小于21: 20.9
decimalMinDecimal 必须大于或等于100.2: 100.1
idCard 身份证账号格式错误: 92002199902137720
sizeStr 个数必须在1和3之间: size
emptyList 不能为空: null
passTime 需要是一个将来的时间: Fri Apr 19 18:06:18 CST 2019
blankStr 不能为空:
digits 数字的值超出了允许范围(只允许在3位整数和2位小数范围内): 999.212
public class BeanGraphForValidate {
@NotEmpty
private List<@Valid BeanForValidate> beanForValidates; // Bean Validation 2.0
// OR
// @NotEmpty
// @Valid 嵌套校验需要指定 @Valid注解
// private List beanForValidatess;
@NotEmpty
private Map beanForValidateMap; // Bean Validation 2.0
// OR
// @NotEmpty
// @Valid
// private Map beanForValidateMapM;
{
beanForValidates = new ArrayList<>();
BeanForValidate beanForValidate = new BeanForValidate();
beanForValidates.add(beanForValidate);
beanForValidateMap = new HashMap<>();
beanForValidateMap.put("map", beanForValidate);
}
}
校验:
ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
.configure().failFast(false).buildValidatorFactory();
Validator validator = validatorFactory.getValidator();
BeanGraphForValidate beanGraphForValidate = new BeanGraphForValidate();
Set> result = validator.validate(beanGraphForValidate);
List message
= result.stream().map(v -> v.getPropertyPath() + " " + v.getMessage() + ": " + v.getInvalidValue())
.collect(Collectors.toList());
for (String str : message) {
System.out.println(str);
}
结果:
beanForValidateMap[map].emptyList 不能为空: null
beanForValidates[0].sizeStr validated size size: size
beanForValidateMap[map].blankStr 不能为空:
beanForValidateMap[map].property 不能为空: null
beanForValidateMap[map].passTime 需要是一个将来的时间: Mon Apr 22 00:44:45 CST 2019
beanForValidates[0].minDecimal the element is 20.9, but need element < 21: 20.9
beanForValidateMap[map].minMaxInt 最小不能小于11: 10
beanForValidates[0].decimalMinDecimal 必须大于或等于100.2: 100.1
beanForValidateMap[map].idCard 身份证账号格式错误: 92002199902137720
beanForValidates[0].positive must be greater than 0: -1
beanForValidateMap[map].digits 数字的值超出了允许范围(只允许在3位整数和2位小数范围内): 99.212
beanForValidates[0].minMaxInt 最小不能小于11: 10
beanForValidateMap[map].minDecimal the element is 20.9, but need element < 21: 20.9
beanForValidates[0].idCard 身份证账号格式错误: 92002199902137720
beanForValidates[0].property 不能为空: null
beanForValidates[0].passTime 需要是一个将来的时间: Mon Apr 22 00:44:45 CST 2019
beanForValidateMap[map].sizeStr validated size size: size
beanForValidates[0].blankStr 不能为空:
beanForValidates[0].digits 数字的值超出了允许范围(只允许在3位整数和2位小数范围内): 99.212
beanForValidateMap[map].positive must be greater than 0: -1
beanForValidateMap[map].decimalMinDecimal 必须大于或等于100.2: 100.1
beanForValidates[0].emptyList 不能为空: null
public class BeanForGroupValidate {
interface ListGroup {
}
interface MapGroup {
}
@NotNull(groups = ListGroup.class, message = "listGroup") // 用于指定需要校验的组
private List list;
@NotNull(groups = MapGroup.class, message = "mapGroup")
private Map map;
@NotNull(message = "belong defaultGroup") // 未指明分组则为缺省组 Default.class
private Map defaultMap;
}
校验:
validator.validate(beanGraphForValidate)
结果:
defaultMap belong to defaultGroup: null
校验:validator.validate(beanGraphForValidate, BeanForGroupValidate.ListGroup.class)
结果:
list listGroup: null
校验:
validator.validate(beanGraphForValidate, BeanForGroupValidate.MapGroup.class)
结果:
map mapGroup: null
public class MethodValidate {
@NotBlank
private String name;
@NotNull
private Integer age;
@NotNull
public MethodValidate(String name) {
this.name = name;
}
public MethodValidate(@NotNull Integer age) {
this.age = age;
}
@Size(min = 2, max = 2)
public List getPeopleName() {
List peoples = new ArrayList<>();
peoples.add(name);
return peoples;
}
public void setPeopleName(@NotBlank String name) {
this.name = name;
}
}
校验:
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
ExecutableValidator executableValidator = factory.getValidator().forExecutables();
MethodValidate methodValidate = new MethodValidate("Mary");
Method method = MethodValidate.class.getMethod( "setPeopleName", String.class );
Object[] parameterValues = { "" };
Set> validateSetPeopleName = executableValidator.validateParameters(
methodValidate,
method,
parameterValues
);
List messages
= validateSetPeopleName.stream().map(v -> v.getPropertyPath() + " " + v.getMessage() + ": " + v.getInvalidValue())
.collect(Collectors.toList());
System.out.println(messages);
method = MethodValidate.class.getMethod("getPeopleName");
List peoples = new ArrayList<>();
peoples.add("Mary");
Set> validateGetPeopleName = executableValidator.validateReturnValue(
methodValidate,
method,
peoples
);
messages
= validateGetPeopleName.stream().map(v -> v.getPropertyPath() + " " + v.getMessage() + ": " + v.getInvalidValue())
.collect(Collectors.toList());
System.out.println(messages);
Constructor constructorParams = MethodValidate.class.getConstructor(Integer.class);
Object[] params = {null};
Set> validateConstructor = executableValidator.validateConstructorParameters(
constructorParams,
params
);
messages
= validateConstructor.stream().map(v -> v.getPropertyPath() + " " + v.getMessage() + ": " + v.getInvalidValue())
.collect(Collectors.toList());
System.out.println(messages);
Constructor constructorReturn = MethodValidate.class.getConstructor(String.class);
MethodValidate createdBean = new MethodValidate(18);
Set> validateConstructorReturn = executableValidator.validateConstructorReturnValue(
constructorReturn,
createdBean
);
messages
= validateConstructorReturn.stream().map(v -> v.getPropertyPath() + " " + v.getMessage() + ": " + v.getInvalidValue())
.collect(Collectors.toList());
System.out.println(messages);
结果:
[setPeopleName.arg0 不能为空: ]
[getPeopleName. 个数必须在2和2之间: [Mary]]
[MethodValidate.arg0 不能为null: null]
[]
如何将Bean Validation集成到Spring MVC框架中?
MAVEN依赖:
org.hibernate
hibernate-validator
6.0.1.Final
org.glassfish
javax.el
3.0.1-b06
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
@Bean
public Validator validator() {
ValidatorFactory vf = Validation.byProvider(HibernateValidator.class)
.configure().failFast(true)
.buildValidatorFactory();
return vf.getValidator();
}
Requirements
Spring 容器中需要创建Bean:LocalValidatorFactoryBean,LocalValidatorFactoryBean包含Bean Validation的实现类(通过SPI加载Bean Validation的实现,本文使用hibernate-validator实现)
使用
@RequestMapping(value = "/beanValidation", method = RequestMethod.POST)
public void testBeanValidation(@Valid BeanForValidate beanForValidate)
SpringMVC在创建RequestMappingHandlerAdapter时会将OptionalValidatorFactoryBean设置到WebBindingInitializer属性中,RequestResponseBodyMethodProcessor在解析参数的时候通过WebBindingInitializer将OptionalValidatorFactoryBean绑定到WebDataBinder中,WebDataBinder通过判断参数是否含有@Valid或@Validated来决定是否进行参数校验
Requirements
Spring 容器中需要创建Bean:MethodValidationPostProcessor
使用
@Validated
public interface BeanValidationService {
void testBeanValidation(@Valid BeanForValidate beanForValidate);
我的博客