简介
Hibernate Validator 是 Java Bean Validation 规范的一个实现,提供了对 Java 类字段的全面验证功能。通过注解,开发者可以灵活地对对象属性进行校验,确保数据的有效性和一致性。它与 Spring 框架紧密集成,支持使用 @Valid 注解进行自动校验。采用 Hibernate Validator 可以使参数校验更加简洁优雅,提升代码的可维护性、健壮性和可读性。 官网
引入依赖
<dependencies>
<!-- Hibernate Validator 依赖 -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>7.0.4.Final</version>
</dependency>
<!-- Bean Validation API 依赖 -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>javax.validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<!-- 为了支持 Spring 验证,通常需要添加以下依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>2.5.0</version>
</dependency>
</dependencies>
配置验证
一旦依赖添加完成,就可以开始使用Hibernate Validator来验证你的Java Bean中的数据。Hibernate Validator使用注解来声明验证规则,常用的注解有:@NotNull、@Size、@Length、@Max、@Pattern等。
定义验证规则
public class User {
@NotNull(message = "Username cannot be null")
@Size(min = 3, max = 50, message = "Username must be between 3 and 50 characters")
private String username;
@Email(message = "Invalid email address")
private String email;
@Min(value = 18, message = "Age must be at least 18")
private int age;
}
校验
@RestController
@RequestMapping("/users")
public class UserController {
@PostMapping
public ResponseEntity<?> createUser(@RequestBody @Valid User user, BindingResult result) {
if (result.hasErrors()) {
return ResponseEntity.badRequest().body(result.getAllErrors());
}
return ResponseEntity.ok("User created successfully!");
}
}
如果非spring环境,就是普通Java应用程序中使用Hibernate Validator进行验证,那么需要手动构建验证器,并手动执行验证。
public class ValidatorUtil {
private static Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
public static void validate(Object obj, Class<?> groups) {
// 执行验证
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
if (!constraintViolations.isEmpty()) {
ArrayList<String> list = new ArrayList<>();
for (ConstraintViolation<Object> constraintViolation : constraintViolations) {
list.add(constraintViolation.getPropertyPath().toString() + "->" + constraintViolation.getMessage());
}
throw new BizException(ResponseEnum.PARAM_CHECK_EXCEPTION, JSON.toJSONString(list));
}
}
}
自定义验证器
除了内置的注解,Hibernate Validator还支持自定义验证,可以自定义验证逻辑并通过注解使用它。
定义验证注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.PARAMETER })
@Constraint(validatedBy = EnumValidator.class)
public @interface EnumValidation {
String message() default "Invalid error";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
Class<?> clazz();
String method();
}
定义验证逻辑
public class EnumValidator implements ConstraintValidator<EnumValidation, Object> {
private EnumValidation annotation;
// 初始化(如果需要)
@Override
public void initialize(EnumValidation constraintAnnotation) {
this.annotation = constraintAnnotation;
}
// 自定义验证逻辑
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
if (value == null) return true;
if (value instanceof String) {
if (StringUtils.isBlank(String.valueOf(value))) {
return true;
}
}
Object[] objects = annotation.clazz().getEnumConstants();
try {
Method method = annotation.clazz().getMethod(annotation.method());
for (Object o : objects) {
if (method.invoke(o).equals(value)) {
return true;
}
}
} catch (Exception e) {
throw new BizException(e);
}
return false;
}
}
国际化
Hibernate Validator 支持国际化 (i18n),可以根据不同的语言环境提供不同的错误消息。通过使用资源文件(如 .properties 文件)来管理不同语言的错误信息,开发者可以让验证错误提示信息根据用户的语言选择来变化。
配置国际化
1.定义验证规则
public class User {
@NotNull(message = "{user.name.not.null}")
@Size(min = 3, max = 50, message = "{user.name.length.limit}")
private String username;
@Email(message = "{email.format.limt}")
private String email;
@Min(value = 18, message = "{age.min.limit}")
private int age;
}
2.多语言资源文件定义
validation_zh.properties
user.name.not.null=Username cannot be null
user.name.length.limit=Username must be between {min} and {max} characters
email.format.limt=Invalid email address
age.min.limit=Age must be at least {value}
validation_en.properties
user.name.not.null=用户名不能为空
user.name.length.limit=用户名长度必须在{min}-{max}个字符内
email.format.limt=邮箱格式无效
age.min.limit=年龄必须大于18
3.多语言环境配置
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Bean
public FilterRegistrationBean requestContextFilter(){
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
// RequestContextFilter doFilterInternal的initContextHolders方法将请求头的Accept-Language定义的语言设置到LocaleContextHolder
// 这样LocaleContextMessageInterpolator#interpolate就可以获取到当前语言
RequestContextFilter requestContextFilter = new RequestContextFilter();
registrationBean.setFilter(requestContextFilter);
registrationBean.addUrlPatterns("/*");
registrationBean.setName("requestContextFilter");
registrationBean.setOrder(1);
return registrationBean;
}
}
Valid注解方式验证
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public Validator getValidator() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
// 设置文件字符编码
messageSource.setDefaultEncoding("UTF-8");
// 设置多语言文件路径,validation_zh.properties、validation_en.properties、validation.properties(未匹配时默认使用)
messageSource.setBasenames("i18n/validation");
LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean();
localValidatorFactoryBean.setMessageInterpolator(new LocaleContextMessageInterpolator(new ResourceBundleMessageInterpolator(new MessageSourceResourceBundleLocator(messageSource))));
return localValidatorFactoryBean;
}
}
编程式验证
public class ValidatorUtil {
private static Validator validator;
static {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
// 设置文件字符编码
messageSource.setDefaultEncoding("UTF-8");
// 设置多语言文件路径
messageSource.setBasenames("i18n/validation");
MessageInterpolator messageInterpolator = new ResourceBundleMessageInterpolator(new MessageSourceResourceBundleLocator(messageSource));
validator = Validation.byDefaultProvider()
.configure()
// 多语言拦截处理
.messageInterpolator(messageInterpolator)
.buildValidatorFactory().getValidator();
}
public static void validate(Object obj, Class<?> groups) {
// 执行验证
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
if (!constraintViolations.isEmpty()) {
ArrayList<String> list = new ArrayList<>();
for (ConstraintViolation<Object> constraintViolation : constraintViolations) {
list.add(constraintViolation.getPropertyPath().toString() + "->" + constraintViolation.getMessage());
}
throw new BizException(ResponseEnum.PARAM_CHECK_EXCEPTION, JSON.toJSONString(list));
}
}
}
自定义资源
如果资源文件需要通过其他方式获取,比如数据库,那么通过ResourceBundleLocator接口来实现,替换MessageSourceResourceBundleLocator。
public class DatabaseResourceBundleLocator implements ResourceBundleLocator {
@Override
public ResourceBundle getResourceBundle(Locale locale) {
// 从数据库中加载资源
Properties properties = loadResourceBundleFromDatabase(locale);
return new DatabaseResourceBundle(properties);
}
private Properties loadResourceBundleFromDatabase(Locale locale) {
// 编写从数据库加载资源的逻辑
return null;
}
private static class DatabaseResourceBundle extends ResourceBundle {
private final Properties properties;
public DatabaseResourceBundle(Properties properties) {
this.properties = properties;
}
@Override
protected Object handleGetObject(String key) {
return properties.get(key);
}
@Override
public Enumeration<String> getKeys() {
return properties.stringPropertyNames().asIterator();
}
}
}