本文主要讲述如何使用hibernate validator来校验入参,避免在业务代码里进行每个接口进行入参校验,提搞代码的简洁及欣赏性。 主要涉及针对接口方法的入参简单校验,不涉及使用group进行的组合校验及其他。
一、POM依赖
<!-- dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.13.Final</version>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>3.0.1-b06</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>2.2.6</version>
</dependency>
注:hibernate-validator-6.0.13.Final
本身依赖validation-api
的版本即是2.0.1.Final
,若无冲突,则无需单独显式依赖validation-api-2.0.1.Final
二、初始化Validator
2.1 方法一 注册Bean实现
import org.hibernate.validator.HibernateValidator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
@Configuration
public class ValidatorConfig {
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor(@Qualifier("validator") Validator validator) {
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
processor.setValidator(validator); // 若不定制validator,此处可不用set
return processor;
}
@Bean
public Validator validator() {
ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
.configure()
.failFast(true)
.buildValidatorFactory();
return validatorFactory.getValidator();
}
}
2.2 方法二 手动实例化validator
ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
.configure()
.failFast(true)
.buildValidatorFactory();
Validator validator = validatorFactory.getValidator();
2.3 补充说明
failFast
默认为false
,即会校验完全部参数后再返回全部参数的校验结果信息.failFast(true)
也可用.addProperty("hibernate.validator.fail_fast", "true")
替代,若是用的defaultProvider,即Validation.byDefaultProvider()
,则只能使用后者
三、校验接口方法入参
3.1 方法一
若是通过上面方法一实例化validator,则不需要手动校验
3.2 方法二
若是使用的方法二,则可通过下面方法来校验方法入参
Set<ConstraintViolation<Object>> constraintViolationSet = validator.forExecutables().validateParameters(bean, method, args);
- 入参
bean
是接口实现bean
,非入参bean
- 也可使用
validator.validate(arg)
来遍历校验单个参数,但要求arg
不能为null
,有些接口方法是平铺入参,且部分入参可为null
的则不适应
四、获取校验失败返回信息
可以通过AOP或Filter来处理方法入参校验
4.1 获取校验结果
- 若是通过上面方法一,则直接捕捉
javax.validation.ConstraintViolationException
异常即可,然后e.getConstraintViolations()
即可获取到Set<ConstraintViolation>
- 若是通过上面方法二,则校验结果直接返回了
Set<ConstraintViolation>
4.2 拼装校验信息
// constraintViolationSet = 上面校验结果Set
if (constraintViolationSet.isEmpty()) {
return null;
}
StringBuilder errorMsg = new StringBuilder();
for (ConstraintViolation violation : constraintViolationSet) {
errorMsg.append(";");
errorMsg.append(violation.getMessage());
}
return errorMsg.substring(1);
五、使用示例
5.1 入参类
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
public class User {
@NotBlank(message = "名称不能为空")
private String name;
@NotNull(message = "年龄不能为空")
@Min(value = 1, message = "年龄不能小于{value}")
private Integer age;
// .... getters and setters
}
5.2 接口方法
// 接口
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
public interface UserService {
void register1(@NotNull(message = "请求入参不能为空") @Valid User user);
void register2(@NotBlank(message = "名称不能为空") String name, Integer age);
}
// 实现类
import org.springframework.validation.annotation.Validated;
@Validated
public class UserServiceImpl implements UserService {
public void register1(User user) {
// ....
}
public void register2(String name, Integer age) {
// ....
}
}
- 若入参是个大参数,如register1的入参,则必须加
@Valid
注解,否则大参数里的属性不会校验 - 实现类上必须加
@Validated
注解
六、FAQ
6.1 若不指定校验失败的message,则会返回什么
若不指定message,则会返回默认的message,即注解的message默认值,如@NotNull
注解默认message占位符是{javax.validation.constraints.NotNull.message}
,如下图
根据占位符可搜索到在hibernate-validator
包下的Resource配置文件中,如下图
里面有对应的中文版本,如下图
上图上有各种语言版本,针对中文版的内容使用ASCII码,可通过工具转成native查看具体的message。
6.2 报错“Caused by: java.lang.NoClassDefFoundError: org/hibernate/validator/internal/engine/DefaultClockProvider”
原因是包冲突,根本原因是引入hibernate-validator依赖包后出现了validation-api的1.1.0.Final版本,应用中依赖的spring-boot中声明了validation-api-1.1.0.Final
包,所以在引入时出现了冲突。
解决:可以显示在parent的POM中显示依赖validation-api-2.0.1.Final
即可。
6.3 报错“HV000183: Unable to initialize 'javax.el.ExpressionFactory'. Check that you have the EL dependencies on the classpath, or use ParameterMessageInterpolator instead”
原因是没有依赖el相关包,增加如下包依赖即可
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>3.0.1-b06</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>2.2.6</version>
</dependency>
6.4 如何依赖一个包即可包含上面包的所有依赖
可依赖以下包,即包含所有依赖,若有依赖包冲突则同样需单独处理
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
上面spring-boot-starter-validation
包无其他逻辑,仅单纯依赖了hibernate-validator-6.0.13.Final
、tomcat-embed-el-8.5.31
和spring-boot-starter-1.5.13.RELEASE
3个包
来源:oschina
链接:https://my.oschina.net/u/812874/blog/2873225