在java项目中,我们经常需要对java对象的格式进行验证,比如接口传进来的User对象的name不能为空,我们传出去的数据的数据要符合规范,传出去前先手动验证, 如果用代码一个一个判断,那可太low了,幸好Spring项目中含有相应的工具。
手动验证
怎么判断一个对象是否符合注解说明的格式?上代码
场景:一个学生有个书包,学生和书包的名字都不能为空
@Data public class Student { @NotBlank(message = "学生名字不能为空") private String name; @NotNull @Min(0) private Integer age; @Valid @NotNull private Bag bag; } @Data public class Bag { private Integer weight; @NotBlank(message = "书包名字不能为空") private String name; } private static Validator validator = Validation.buildDefaultValidatorFactory() .getValidator(); public static void main(String[] args) { Bag bag = new Bag(); bag.setName(""); bag.setWeight(343); Student student = new Student(); student.setAge(343); student.setName("fdsfs"); student.setBag(bag); Set<ConstraintViolation> set = validator.validate(student, Default.class); if (set != null && set.size() > 0) { for (ConstraintViolation cv : set) { System.out.println("===="); System.out.println(cv); } } }
当需要对嵌套的对象也进行验证的时候,需要在被嵌套的属性上加上 @Valid注解 ,默认如果嵌套的属性为null,那就不验证它了,如果你想验证它不为null,加@NotNull注解。
spring 框架中使用的 Validator
spring 框架中也有同样的代码,主要有两块:
1,添加到注入的Component里面,spring 初始化容器对象的时候会进行验证,
org/springframework/validation/beanvalidation/BeanValidationPostProcessor.java
@Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (this.afterInitialization) { doValidate(bean); } return bean; } /** * Perform validation of the given bean. * @param bean the bean instance to validate * @see javax.validation.Validator#validate */ protected void doValidate(Object bean) { Assert.state(this.validator != null, "No Validator set"); Set<ConstraintViolation<Object>> result = this.validator.validate(bean); if (!result.isEmpty()) { StringBuilder sb = new StringBuilder("Bean state is invalid: "); for (Iterator<ConstraintViolation<Object>> it = result.iterator(); it.hasNext();) { ConstraintViolation<Object> violation = it.next(); sb.append(violation.getPropertyPath()).append(" - ").append(violation.getMessage()); if (it.hasNext()) { sb.append("; "); } } throw new BeanInitializationException(sb.toString()); } }
这个BeanPostProcessor就是在每个bean被创建的时候,属性设置完成后,拦截一下,验证一下这个bean的属性符不符合注解的要求,不符合就扔异常
2,controller,service方法参数里面,对参数验证
org/springframework/validation/beanvalidation/MethodValidationInterceptor.java
@Override @SuppressWarnings("unchecked") public Object invoke(MethodInvocation invocation) throws Throwable { Class<?>[] groups = determineValidationGroups(invocation); // Standard Bean Validation 1.1 API ExecutableValidator execVal = this.validator.forExecutables(); Method methodToValidate = invocation.getMethod(); Set<ConstraintViolation<Object>> result; try { result = execVal.validateParameters( invocation.getThis(), methodToValidate, invocation.getArguments(), groups); } catch (IllegalArgumentException ex) { // Probably a generic type mismatch between interface and impl as reported in SPR-12237 / HV-1011 // Let's try to find the bridged method on the implementation class... methodToValidate = BridgeMethodResolver.findBridgedMethod( ClassUtils.getMostSpecificMethod(invocation.getMethod(), invocation.getThis().getClass())); result = execVal.validateParameters( invocation.getThis(), methodToValidate, invocation.getArguments(), groups); } if (!result.isEmpty()) { throw new ConstraintViolationException(result); } Object returnValue = invocation.proceed(); result = execVal.validateReturnValue(invocation.getThis(), methodToValidate, returnValue, groups); if (!result.isEmpty()) { throw new ConstraintViolationException(result); } return returnValue; }
这个拦截的是被 @Validated注解的类上的方法,对方法的入参进行一个拦截,
当然第二个其实我不是看的懂,以后更新。