最近写业务代码,由于页面复杂,致使对应的Bean属性很是多,而产品大佬又提出各类校验要求。html
emmmmmm......若是写if条件来校验,那简直是又臭又长。java
因此就有今天的话题——利用注解对Bean进行校验。git
利用注解对Bean进行校验,主要是利用hibernate-validator框架,hibernate-validator实现了validation-api的接口关于Bean校验的接口,直接使用hibernate-validator很是方便,省时省力。web
此hibernate-validator+SpringMVC能够实现如下功能:正则表达式
优点:spring
实现要求:apache
<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>
hibernate validator使用的注解定义在hibernate-validator-6.0.1.Final.jar、validation-api-1.1.0.Final.jar两个包中,bootstrap
@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格式
利用hibernate validator实现Bean校验,通常有两种作法:api
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 ... }
验证器
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
校验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
校验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的使用方式),能够发现二、3大同小异,使用@Valid能够不用再本身写校验的处理方法,可是方式1能够更加灵活,能够按照本身需求处理和组织校验的返回信息。服务器
hibernate validator的更详细的使用说明,能够参考官方文档,之前没关注,此次认真的看了一下,发现hibernate validator的功能仍是很强大的,最新的5.0、6.0版本只有英文文档,4.0版本有中文文档。地址以下:https://docs.jboss.org/hibern...
使用方式1,在我本身电脑注解没有添加message信息时,默认返回的是中文,如@NotNull:不能为null,@NotEmpty:不能为空
等等,可是,当部署到服务器之后,就变成英文,如@NotNull:may not be null,@NotEmpty:may not be empty
等等,非常诡异。后来研读源码发现,hibernate validator经过messageInterpolator
字段来实现国际化的,messageInterpolator
字段中存放着当前操做系统的语言环境参数,而后根据语言环境参数去对应的配置文档中读取默认提示信息,而messageInterpolator
则会在项目启动时初始化,由于我本身的电脑是中文环境,而公司的系统环境是英文的,因此致使这个问题。