最近写业务代码,因为页面复杂,导致对应的Bean属性非常多,而产品大佬又提出各种校验要求。
emmmmmm......如果写if条件来校验,那简直是又臭又长。
所以就有今天的话题——利用注解对Bean进行校验。
利用注解对Bean进行校验,主要是利用hibernate-validator框架,hibernate-validator实现了validation-api的接口关于Bean校验的接口,直接使用hibernate-validator非常方便,省时省力。
1.hibernate-validato简介
此hibernate-validator+SpringMVC可以实现以下功能:
- 注解java bean声明校验规则;
- 添加message错误信息源实现国际化配置;
- 结合spring form中的errors标签展现错误信息。
优势:
- 代码简洁、处理校验可以更加优雅。
实现要求:
- 使用hibernate validator至少要引入两个jar包:
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.el</artifactId>
<version>3.0.1-b08</version>
</dependency>
- java bean中使用注解添加检验规则。
2.注解及用法
hibernate validator使用的注解定义在hibernate-validator-6.0.1.Final.jar、validation-api-1.1.0.Final.jar两个包中,
@AssertFalse 验证注解的元素值是false
@AssertTrue 验证注解的元素值是true
@DecimalMax(value=x) 验证注解的元素值小于等于@ DecimalMax指定的value值
@DecimalMin(value=x) 验证注解的元素值小于等于@ DecimalMin指定的value值
@Digits(integer=整数位数, fraction=小数位数) 验证注解的元素值的整数位数和小数位数上限
@Future 验证注解的元素值(日期类型)比当前时间晚
@Max(value=x) 验证注解的元素值小于等于@Max指定的value值
@Min(value=x) 验证注解的元素值大于等于@Min指定的value值
@NotNull 验证注解的元素值不是null
@Null 验证注解的元素值是null
@Past 验证注解的元素值(日期类型)比当前时间早
@Pattern(regex=正则表达式, flag=) 验证注解的元素值与指定的正则表达式匹配
@Size(min=最小值, max=最大值) 验证注解的元素值的在min和max(包含)指定区间之内,如字符长度、集合大小
@Valid 验证关联的对象,如账户对象里有一个订单对象,指定验证订单对象
@NotEmpty 验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0)
@Range(min=最小值, max=最大值) 验证注解的元素值在最小值和最大值之间
@NotBlank 验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的空格
@Length(min=下限, max=上限) 验证注解的元素值长度在min和max区间内
@Email 验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式
3.校验demo
利用hibernate validator实现Bean校验,一般有两种做法:
- 利用@ModelAttribute和@Valid注解;
- 利用validate()、validateValue()、validateProperty()等方法在需要之处直接调用。
Bean对象:
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
public class Car {
@NotNull
private String manufacturer;
@NotNull
@Size(min = 2, max = 14)
private String licensePlate;
@Min(2)
private int seatCount;
public Car(String manufacturer, String licencePlate, int seatCount) {
this.manufacturer = manufacturer;
this.licensePlate = licencePlate;
this.seatCount = seatCount;
}
//getters and setters ...
}
3.1.利用Validator的validate()方法
验证器
import org.apache.commons.collections4.CollectionUtils;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.util.Set;
public class ValidateTest {
/**
* 验证器
*/
private static Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
public static void main(String[] args) {
Car car = new Car(null, "12", 1);
//执行验证
Set<ConstraintViolation<Car>> constraintViolations = validator.validate(car);
//打印校验信息
if (CollectionUtils.isNotEmpty(constraintViolations)) {
for (ConstraintViolation<Car> constraintViolation : constraintViolations) {
System.out.println(constraintViolation.getPropertyPath().toString() + ": " + constraintViolation.getMessage());
}
}
}
}
输出结果:
seatCount: 最小不能小于2
manufacturer: 不能为null
3.2.利用@ModelAttribute和@Valid注解和BindingResult对象
校验demo
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.validation.Valid;
@Controller("/Validate")
public class ValidateTestController {
@RequestMapping("/demo1")
public void validateDemo(@Valid @ModelAttribute("car") Car car, BindingResult result) {
if(result.hasErrors()){
for (ObjectError error : result.getAllErrors()) {
System.out.println(error.getDefaultMessage());
}
}
}
}
测试数据:
{
"manufacturer": null,
"licensePlate": "",
"seatCount": 1
}
结果:
不能为null
个数必须在2和14之间
最小不能小于2
注意,需要加上@ModelAttribute注解,否则会报错: An Error/BindingResult argument is
expected to be declared immediately after the model attribute
3.3.利用@Valid注解和统一异常处理
校验demo
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.validation.Valid;
@Controller
@RequestMapping("/validate")
public class ValidateTestController {
@RequestMapping("/demo1")
public void validateDemo(@Valid Car car) { }
}
测试数据:
{
"manufacturer": null,
"licensePlate": "",
"seatCount": 1
}
统一异常处理类:
@Component
public class ExceptionResolver implements HandlerExceptionResolver {
/**
* 日志
*/
private static final Logger LOG = LoggerFactory.getLogger(AuditCommonExceptionResolver.class);
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
if (ex instanceof BindException) {
BindException exs = (BindException) ex;
for (ObjectError o : exs.getAllErrors()) {
System.out.println(o.getDefaultMessage());
}
} else if (ex instanceof ConstraintViolationException) {
ConstraintViolationException exs = (ConstraintViolationException) ex;
Set<ConstraintViolation<?>> violations = exs.getConstraintViolations();
for (ConstraintViolation<?> item : violations) {
System.out.println(item.getMessage());
}
} else {
LOG.error("捕获未处理异常,异常信息:{}", ex.getMessage());
}
return new ModelAndView("/sys/errorPage.jsp");
}
}
结果:
不能为null
个数必须在2和14之间
最小不能小于2
注意:我自己测试的时候,捕获的是BindException异常,但是看网上好多文章写的都是捕获ConstraintViolationException异常,
比较以上三种校验方式(准确的说应该是三种hibernate validator的使用方式),可以发现2、3大同小异,使用@Valid可以不用再自己写校验的处理方法,但是方式1可以更加灵活,可以按照自己需求处理和组织校验的返回信息。
4.hibernate validator更详细的使用文档
hibernate validator的更详细的使用说明,可以参考官方文档,以前没关注,这次认真的看了一下,发现hibernate validator的功能还是很强大的,最新的5.0、6.0版本只有英文文档,4.0版本有中文文档。地址如下:https://docs.jboss.org/hibern...
5.使用过程中遇到的问题
使用方式1,在我自己电脑注解没有添加message信息时,默认返回的是中文,如@NotNull:不能为null,@NotEmpty:不能为空
等等,但是,当部署到服务器以后,就变成英文,如@NotNull:may not be null,@NotEmpty:may not be empty
等等,很是诡异。后来研读源码发现,hibernate validator通过messageInterpolator
字段来实现国际化的,messageInterpolator
字段中存放着当前操作系统的语言环境参数,然后根据语言环境参数去对应的配置文档中读取默认提示信息,而messageInterpolator
则会在项目启动时初始化,因为我自己的电脑是中文环境,而公司的系统环境是英文的,所以导致这个问题。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。