本文目录导读:

我来通过一个完整的案例演示 Hibernate Validator 的使用。
项目结构
demo/
├── pom.xml
└── src/
└── main/
└── java/
└── com/
└── example/
├── User.java
├── UserService.java
├── UserController.java
├── CustomValidator.java
└── ValidatorUtil.java
Maven 依赖
<dependencies>
<!-- Hibernate Validator -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>8.0.1.Final</version>
</dependency>
<!-- 表达式语言依赖 -->
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>jakarta.el</artifactId>
<version>4.0.2</version>
</dependency>
<!-- 实体类如需要使用注解 -->
<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
<version>3.1.0</version>
</dependency>
<!-- Json 处理(用于 Controller 示例) -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
</dependencies>
基础实体类示例
package com.example;
import jakarta.validation.constraints.*;
import jakarta.validation.constraints.Pattern.Flag;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
/**
* 用户实体类,展示各种常用约束
*/
public class User {
@NotNull(message = "用户ID不能为空")
@Min(value = 1, message = "用户ID必须大于0")
@Max(value = 99999, message = "用户ID不能超过99999")
private Long id;
@NotBlank(message = "用户名不能为空")
@Size(min = 3, max = 20, message = "用户名长度必须在3-20之间")
@Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "用户名只能包含字母、数字和下划线")
private String username;
@NotBlank(message = "密码不能为空")
@Size(min = 6, max = 32, message = "密码长度必须在6-32之间")
@Pattern(regexp = "^(?=.*[0-9])(?=.*[a-zA-Z]).{6,32}$",
message = "密码必须同时包含字母和数字")
private String password;
@Email(message = "邮箱格式不正确")
@NotBlank(message = "邮箱不能为空")
private String email;
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
private String phone;
@Min(value = 0, message = "年龄不能为负数")
@Max(value = 150, message = "年龄不能超过150")
private Integer age;
@DecimalMin(value = "0.0", message = "工资不能为负数")
@DecimalMax(value = "999999.99", message = "工资不能超过999999.99")
@Digits(integer = 6, fraction = 2, message = "工资格式不正确")
private BigDecimal salary;
@NotNull(message = "出生日期不能为空")
@Past(message = "出生日期必须是过去的日期")
private LocalDate birthDate;
@Future(message = "到期日期必须是未来的日期")
private LocalDate expiryDate;
@AssertTrue(message = "必须同意用户协议")
private boolean agree;
@NotEmpty(message = "角色列表不能为空")
@Size(min = 1, max = 5, message = "角色数量必须在1-5之间")
private List<@NotBlank(message = "角色不能为空") String> roles;
// getters and setters...
}
分组验证示例
package com.example;
import jakarta.validation.Valid;
import jakarta.validation.constraints.*;
import jakarta.validation.groups.Default;
/**
* 使用分组验证的用户类
*/
public class GroupUser {
// 定义分组接口
public interface AddGroup {}
public interface UpdateGroup {}
public interface QueryGroup {}
@NotNull(groups = UpdateGroup.class, message = "更新时ID不能为空")
@Null(groups = AddGroup.class, message = "新增时ID必须为空")
private Long id;
@NotBlank(groups = {AddGroup.class, UpdateGroup.class},
message = "用户名不能为空")
@Size(min = 3, max = 20,
groups = {AddGroup.class, UpdateGroup.class, QueryGroup.class},
message = "用户名长度必须在3-20之间")
private String username;
@NotBlank(groups = AddGroup.class, message = "新增时密码不能为空")
@Size(min = 6, max = 32, groups = AddGroup.class,
message = "密码长度必须在6-32之间")
private String password;
@NotBlank(groups = {AddGroup.class, UpdateGroup.class},
message = "邮箱不能为空")
@Email(groups = Default.class, message = "邮箱格式不正确")
private String email;
// 自定义验证方法(分组方法级验证)
@AssertTrue(groups = AddGroup.class, message = "新增时必须是活跃用户")
public boolean isActive() {
return active;
}
private boolean active;
// getters and setters...
}
自定义验证器
package com.example;
import jakarta.validation.Constraint;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import jakarta.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 自定义注解
@Documented
@Constraint(validatedBy = PasswordValidator.class)
@Target({ ElementType.TYPE, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidPassword {
String message() default "密码格式不正确,需要包含大小写字母和数字";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
// 参数:是否必须包含大小写
boolean requireUppercase() default true;
boolean requireLowercase() default true;
boolean requireDigit() default true;
int minLength() default 6;
int maxLength() default 32;
}
// 验证器实现
public class PasswordValidator implements ConstraintValidator<ValidPassword, String> {
private boolean requireUppercase;
private boolean requireLowercase;
private boolean requireDigit;
private int minLength;
private int maxLength;
@Override
public void initialize(ValidPassword constraintAnnotation) {
this.requireUppercase = constraintAnnotation.requireUppercase();
this.requireLowercase = constraintAnnotation.requireLowercase();
this.requireDigit = constraintAnnotation.requireDigit();
this.minLength = constraintAnnotation.minLength();
this.maxLength = constraintAnnotation.maxLength();
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null) {
return false;
}
// 密码长度检查
if (value.length() < minLength || value.length() > maxLength) {
return false;
}
// 必须包含大写字母
if (requireUppercase && !value.matches(".*[A-Z].*")) {
return false;
}
// 必须包含小写字母
if (requireLowercase && !value.matches(".*[a-z].*")) {
return false;
}
// 必须包含数字
if (requireDigit && !value.matches(".*\\d.*")) {
return false;
}
// 禁止特殊字符
if (value.matches(".*[^a-zA-Z0-9].*")) {
return false;
}
return true;
}
}
交叉字段验证示例
package com.example;
import jakarta.validation.Constraint;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import jakarta.validation.Payload;
import java.lang.annotation.*;
// 自定义类级验证注解 - 检查密码确认
@Documented
@Constraint(validatedBy = PasswordMatchValidator.class)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PasswordMatch {
String message() default "密码和确认密码不一致";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String passwordField() default "password";
String confirmPasswordField() default "confirmPassword";
}
// 实现类
public class PasswordMatchValidator
implements ConstraintValidator<PasswordMatch, Object> {
private String passwordField;
private String confirmPasswordField;
@Override
public void initialize(PasswordMatch constraintAnnotation) {
this.passwordField = constraintAnnotation.passwordField();
this.confirmPasswordField = constraintAnnotation.confirmPasswordField();
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
try {
// 通过反射获取字段值
java.lang.reflect.Field passwordField =
value.getClass().getDeclaredField(passwordField);
passwordField.setAccessible(true);
java.lang.reflect.Field confirmPasswordField =
value.getClass().getDeclaredField(confirmPasswordField);
confirmPasswordField.setAccessible(true);
String password = (String) passwordField.get(value);
String confirmPassword = (String) confirmPasswordField.get(value);
// 两个都为 null 或都不为 null 且相等
if (password == null && confirmPassword == null) {
return true;
}
if (password != null && password.equals(confirmPassword)) {
return true;
}
// 构建错误信息
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(
"密码和确认密码不一致"
).addPropertyNode(confirmPasswordField).addConstraintViolation();
return false;
} catch (Exception e) {
return false;
}
}
}
// 使用示例
@PasswordMatch(passwordField = "password", confirmPasswordField = "confirmPassword")
public class RegistrationForm {
@NotBlank(message = "用户名不能为空")
private String username;
@ValidPassword(minLength = 8, maxLength = 20,
requireUppercase = true, requireLowercase = true, requireDigit = true)
private String password;
private String confirmPassword;
@Email(message = "邮箱格式不正确")
private String email;
// getters and setters...
}
服务层验证示例
package com.example;
import jakarta.validation.Valid;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validation;
import jakarta.validation.constraints.*;
import java.util.Set;
/**
* 服务层使用编程式验证
*/
public class UserService {
private final Validator validator;
public UserService() {
// 创建一个验证器
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
this.validator = factory.getValidator();
}
/**
* 编程式验证示例
*/
public void addUser(User user) throws ValidationException {
// 执行验证(使用默认分组)
Set<ConstraintViolation<User>> violations = validator.validate(user);
if (!violations.isEmpty()) {
throw new ValidationException(buildErrorMessage(violations));
}
// 业务逻辑...
System.out.println("用户添加成功: " + user.getUsername());
}
/**
* 指定分组验证
*/
public void updateUser(GroupUser user) throws ValidationException {
// 使用 UpdateGroup 分组进行验证
Set<ConstraintViolation<GroupUser>> violations =
validator.validate(user, GroupUser.UpdateGroup.class);
if (!violations.isEmpty()) {
throw new ValidationException(buildErrorMessage(violations));
}
// 业务逻辑...
System.out.println("用户更新成功");
}
/**
* 方法参数验证(需要配合 AOP 或 Spring)
*/
public void updateUserById(
@NotNull(groups = GroupUser.UpdateGroup.class) Long id,
@Valid GroupUser user) {
// 这种方法级验证通常由 Spring AOP 处理
System.out.println("更新用户 " + id + " 的信息");
}
/**
* 构建错误信息
*/
private String buildErrorMessage(
Set<? extends ConstraintViolation<?>> violations) {
StringBuilder sb = new StringBuilder("验证失败: ");
violations.forEach(v ->
sb.append(v.getPropertyPath())
.append(": ")
.append(v.getMessage())
.append("; ")
);
return sb.toString();
}
/**
* 验证单个属性
*/
public void validateField(String fieldName, Object value) {
Set<? extends ConstraintViolation<?>> violations =
validator.validateValue(User.class, fieldName, value);
if (!violations.isEmpty()) {
throw new ValidationException(buildErrorMessage(violations));
}
}
}
控制器和异常处理
package com.example;
import jakarta.validation.ConstraintViolationException;
import jakarta.validation.Valid;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
/**
* Spring MVC 控制器示例(需要 Spring 环境)
*/
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
/**
* 创建用户 - 使用 @Valid 注解
*/
@PostMapping
public ResponseEntity<?> createUser(@Valid @RequestBody User user) {
try {
userService.addUser(user);
return ResponseEntity.ok(Map.of(
"message", "用户创建成功",
"user", user
));
} catch (Exception e) {
return ResponseEntity.badRequest().body(Map.of(
"error", e.getMessage()
));
}
}
/**
* 更新用户 - 使用 @Validated 和分组
*/
@PutMapping("/{id}")
public ResponseEntity<?> updateUser(@PathVariable Long id,
@Validated @RequestBody GroupUser user) {
user.setId(id);
userService.updateUser(user);
return ResponseEntity.ok(Map.of("message", "更新成功"));
}
/**
* 异常处理 - 处理验证异常
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<?> handleValidationExceptions(
MethodArgumentNotValidException ex) {
Map<String, String> errors = ex.getBindingResult()
.getFieldErrors()
.stream()
.collect(Collectors.toMap(
error -> error.getField(),
error -> error.getDefaultMessage() != null ?
error.getDefaultMessage() : "Invalid value"
));
return ResponseEntity.badRequest().body(Map.of(
"status", HttpStatus.BAD_REQUEST.value(),
"errors", errors
));
}
/**
* 异常处理 - 处理约束违规异常
*/
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<?> handleConstraintViolation(
ConstraintViolationException ex) {
Map<String, String> errors = new HashMap<>();
ex.getConstraintViolations().forEach(v -> {
String field = v.getPropertyPath().toString();
errors.put(field, v.getMessage());
});
return ResponseEntity.badRequest().body(Map.of(
"status", HttpStatus.BAD_REQUEST.value(),
"errors", errors
));
}
}
工具类和测试示例
package com.example;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import jakarta.validation.metadata.BeanDescriptor;
import jakarta.validation.metadata.PropertyDescriptor;
import java.util.Set;
/**
* 验证工具类
*/
public class ValidatorUtil {
private static final Validator validator;
static {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
}
/**
* 验证对象(默认分组)
*/
public static <T> ValidationResult validate(T object) {
Set<ConstraintViolation<T>> violations = validator.validate(object);
return new ValidationResult(violations);
}
/**
* 验证对象(指定分组)
*/
public static <T> ValidationResult validate(T object, Class<?>... groups) {
Set<ConstraintViolation<T>> violations =
validator.validate(object, groups);
return new ValidationResult(violations);
}
/**
* 验证单个属性
*/
public static ValidationResult validateProperty(
Object object, String propertyName) {
Set<ConstraintViolation<Object>> violations =
validator.validateProperty(object, propertyName);
return new ValidationResult(violations);
}
/**
* 获取类的元数据
*/
public static void printClassMetadata(Class<?> clazz) {
BeanDescriptor beanDescriptor = validator.getConstraintsForClass(clazz);
System.out.println("类 " + clazz.getSimpleName() + " 是否包含约束: "
+ beanDescriptor.isBeanConstrained());
for (PropertyDescriptor property : beanDescriptor.getConstrainedProperties()) {
System.out.println("属性: " + property.getPropertyName() +
" 是否有级联验证: " + property.isCascaded());
}
}
/**
* 验证结果类
*/
public static class ValidationResult {
private final Set<? extends ConstraintViolation<?>> violations;
public ValidationResult(Set<? extends ConstraintViolation<?>> violations) {
this.violations = violations;
}
public boolean isValid() {
return violations.isEmpty();
}
public String getErrorMessages() {
if (isValid()) {
return "";
}
return violations.stream()
.map(v -> v.getPropertyPath() + ": " + v.getMessage())
.collect(java.util.stream.Collectors.joining("; "));
}
public Set<? extends ConstraintViolation<?>> getViolations() {
return violations;
}
}
}
// 测试主类
public class Main {
public static void main(String[] args) {
// 创建用户对象进行测试
User user = new User();
user.setUsername("John Doe"); // 用户名包含非法字符
user.setPassword("123"); // 密码太短
user.setEmail("invalid-email"); // 邮箱格式不正确
// 使用工具类验证
ValidatorUtil.ValidationResult result =
ValidatorUtil.validate(user);
if (!result.isValid()) {
System.out.println("验证失败: " + result.getErrorMessages());
}
// 验证单个属性
ValidatorUtil.ValidationResult emailResult =
ValidatorUtil.validateProperty(user, "email");
System.out.println("邮箱验证: " +
(emailResult.isValid() ? "通过" : "失败 - " + emailResult.getErrorMessages()));
// 查看类元数据
ValidatorUtil.printClassMetadata(User.class);
}
}
完整配置(使用 Spring Boot)
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Bean;
import jakarta.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Configuration
public static class ValidatorConfig {
@Bean
public Validator validator() {
return new LocalValidatorFactoryBean();
}
@Bean
public jakarta.validation.ValidatorFactory validatorFactory() {
return jakarta.validation.Validation.buildDefaultValidatorFactory();
}
}
}
高级验证示例
package com.example;
import jakarta.validation.constraints.*;
import java.util.List;
import java.util.Optional;
/**
* 高级验证特性示例
*/
public class AdvancedValidation {
// 1. 嵌套对象验证
public static class Order {
@NotNull(message = "订单ID不能为空")
private Long orderId;
@Valid // 级联验证
private Customer customer;
@Valid
@NotEmpty(message = "订单项不能为空")
private List<@Valid OrderItem> items;
}
public static class Customer {
@NotBlank(message = "客户名不能为空")
private String name;
@Email(message = "客户邮箱格式不正确")
private String email;
}
public static class OrderItem {
@NotBlank(message = "商品名称不能为空")
private String productName;
@Positive(message = "商品数量必须为正数")
private Integer quantity;
@PositiveOrZero(message = "商品价格不能为负数")
private Double price;
}
// 2. Optional 验证
public static class OptionalDemo {
@NotBlank(message = "Optional 值不能为空")
private Optional<String> optionalString;
@Positive(message = "Optional 数值必须为正数")
private Optional<Integer> optionalInteger;
}
// 3. 条件验证
public static class ConditionalValidation {
private String type;
@NotNull(groups = TypeA.class, message = "类型A必须指定")
private String typeAAttribute;
@NotNull(groups = TypeB.class, message = "类型B必须指定")
private String typeBAttribute;
// 分组接口
public interface TypeA {}
public interface TypeB {}
// 自定义组序列
@jakarta.validation.GroupSequence
public interface ValidationOrder extends TypeA, TypeB {}
}
// 4. EL 表达式验证
public static class ELValidation {
@AssertTrue(message = "促销价格必须小于原价")
public boolean isPromotionValid() {
return originalPrice.compareTo(salePrice) > 0;
}
private java.math.BigDecimal originalPrice;
private java.math.BigDecimal salePrice;
}
// 5. 自定义约束组合
@Target({java.lang.annotation.ElementType.FIELD})
@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@NotBlank(message = "电话不能为空")
@Pattern(regexp = "^[0-9-\\s]+$", message = "电话格式不正确")
public @interface ValidPhone {}
}
关键要点
-
常用约束注解:
@NotNull、@NotBlank、@NotEmpty:空值检查@Size、@Min、@Max:大小检查@Pattern:正则表达式验证@Email:邮箱格式验证@Past、@Future:日期验证@Valid:级联验证
-
分组验证:不同场景使用不同验证规则
-
自定义验证器:扩展框架功能
-
交叉字段验证:确保字段间的约束关系
-
Spring Boot 集成:自动配置简化开发
-
异常处理:统一处理验证异常
这个案例覆盖了 Hibernate Validator 的主要功能,可以根据项目需求选择使用相应的功能。