简介
SpringMVC框架提供了SPI扩展:javax.validation.spi.ValidationProvider
,用来实现参数校验功能。Spring使用hibernate-validator作为它的默认实现,我们只需要进行一些简单的注解声明,就可以达到参数校验的功能。但是在实际使用场景中,经常会出现校验没生效的问题。
原因分析
- 检查jar包依赖。
需要确报项目引入hibernate-validator以及与之匹配的validation-api版本,推荐直接依赖spring-boot-starter-validation
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
最好检查下pom实际生效的依赖,有可能因为项目中显示引入了这两个包或者其他包间接依赖了这两个包导致他们版本不匹配。hibernate-validator依赖的validator-api才是它支持的版本。
-
检查注解是否正确
新版本J2EE已经把javax包改成了jarkata包,检查项目中的validation-api中校验注解的包路径,选择与之匹配的注解。
javax.validation.constraints.NotNull
,jakarta.validation.constraints.NotNull
-
检查方法签名中,参数签名是否加了
@Valid
注解,必须加了@Valid
注解的参数才会被校验。 -
检查参数是不是List类型,目前List不会被校验。
-
检查复杂字段上是否有
@Valid
注解,成员变量如果不是基本类型,需要在上面使用@Valid注解。 -
其他情况可以打断点排查:
org.hibernate.validator.internal.engine.ValidatorImpl#validate
解决方案
对于参数是List类型,需要自己新建一个ValidationUtil类,在方法开始处手动调用校验。
java
public class ValidationUtil {
private static final Validator validator;
static {
validator = Validation.buildDefaultValidatorFactory().getValidator();
}
public static void validate(Object object, String objectName) {
if (object instanceof List) {
for (Object obj : (List<?>) object) {
validateEntity(obj, objectName);
}
} else {
validateEntity(object, objectName);
}
}
@SneakyThrows
private static void validateEntity(Object object, String objectName) {
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object);
if (!constraintViolations.isEmpty()) {
throw new MethodArgumentNotValidException(null, createBindingResult(constraintViolations, objectName));
}
}
private static BindingResult createBindingResult(Set<ConstraintViolation<Object>> violations, String objectName) {
List<FieldError> fieldErrors = new ArrayList<>();
for (ConstraintViolation<Object> violation : violations) {
FieldError fieldError = new FieldError(
objectName,
violation.getPropertyPath().toString(),
violation.getMessage()
);
fieldErrors.add(fieldError);
}
return new BeanPropertyBindingResult(null, objectName, false, 256) {
@NotNull
@Override
public List<FieldError> getFieldErrors() {
return fieldErrors;
}
};
}
}
// 调用方法
ValidationUtil.validate(paramValue, "param name");