JSR标准和Spring校验框架
Java
的JSR-303
标准的数据校验的核心接口是javax.validation.Validator
,该接口根据目标对象中标注的校验注解进行数据校验,并得到校验结果。
Spring
也有自己的校验框架,同时支持JSR-303
标准的校验框架。Spring
的DataBinder
在进行数据绑定时,同时调用校验框架完成数据校验工作。
Spring
的校验框架包是org.springframework.validation
,其中LocalValidatorFactoryBean
同时实现了Spring
的Validator
和JSR-303
的Validator
接口,但是Spring
本身没有提供JSR-303
的实现,因此必须将实现了JSR-303
的jar
放在类路径下。
配置校验框架的处理器
<mvc:annotation-driven validator="validator" />
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor" />
上面配置了LocalValidatorFactoryBean
和MethodValidationPostProcessor
两个Bean
,它们的区别:
LocalValidatorFactoryBean
同时实现了Spring
的Validator
和JSR-303
的Validator
接口LocalValidatorFactoryBean
可以完成i18n
MethodValidationPostProcessor
可以实现在方法上校验基本数据包装类型和String
类型等单独的对象,但是要在异常处理器中处理ConstraintViolationException
异常- 两个
Bean
可以同时存在
重要的两个注解@Valid和@Validated
这两个注解都是进行数据校验的标志,但是有区别:
@Valid
是JSR-303标准规定的;@Validated
是Spring校验框架提供的@Valid
可以在方法/成员变量/构造函数/方法参数上使用;@Validated
可以在类/方法/方法参数上使用;区别在于成员变量上是否可以使用- 由于
@Valid
可以在成员变量上使用,因此可以嵌套校验 @Valid
会把校验不通过的信息交给BindingResult
中,因此在controller
的方法中,@Valid
和BindingResult
要同时存在@Validated
可以在类上使用,可以配合MethodValidationPostProcessor
实现校验基本数据包装类型和String
类型等单独的对象
案例
-
依赖
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>5.2.4.Final</version> <exclusions> <exclusion> <artifactId>validation-api</artifactId> <groupId>javax.validation</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>1.1.0.Final</version> </dependency>
-
@Validated
@RestController @RequestMapping("/validated") @Validated public class ValidatedController { @RequestMapping("/test") public Object test(@NotNull(message = "不能为null") String name) { return new Object(); } } @RestControllerAdvice public class ExceptionHandle { @ExceptionHandler(ConstraintViolationException.class) public Object constraintViolationException(ConstraintViolationException e) { StringBuilder stringBuilder = new StringBuilder(); for (ConstraintViolation constraintViolation : e.getConstraintViolations()) { stringBuilder.append(constraintViolation.getMessage()).append(","); } if (stringBuilder.length() > 0) { stringBuilder.deleteCharAt(stringBuilder.length() - 1); } return stringBuilder.toString(); } }
-
@Valid
public class Person implements Serializable { @NotNull(message = "person.name_is_null") private String name; @Valid private Phone phone; public String getName() { return name; } public void setName(String name) { this.name = name; } public Phone getPhone() { return phone; } public void setPhone(Phone phone) { this.phone = phone; } } public class Phone implements Serializable { @NotNull(message = "phone.name_is_null") private String name; private String price; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPrice() { return price; } public void setPrice(String price) { this.price = price; } } @ResponseBody @RequestMapping("/test") public Object test(@Valid Person person, BindingResult result) { if(result != null){ if(result.hasErrors()){ for (FieldError fieldError : result.getFieldErrors()) { System.out.println(fieldError.getDefaultMessage()); } } } return new Object(); }
-
出错的使用
@ResponseBody @RequestMapping("/test") public Object testBindingResult(@NotBlank String name, BindingResult result) { if (result.hasErrors()) { System.out.println(result.getFieldError("name").getDefaultMessage()); } return new Object(); }
会报错:
public Object test(@NotBlank String name, BindingResult result) 严重: Servlet.service() for servlet [springmvc] in context with path [] threw exception [Request processing failed; nested exception is java.lang.IllegalStateException: An Errors/BindingResult argument is expected to be declared immediately after the model attribute, the @RequestBody or the @RequestPart arguments to which they apply: public com.p7.framework.common.ResultBean com.p7.framework.controller.BindingResultController.testBindingResult(java.lang.String,org.springframework.validation.BindingResult)]