本文目录导读:

我来为您提供几个Java验证框架的完整案例,主要涵盖Hibernate Validator(最常用)和Spring Boot Validation。
Hibernate Validator 完整案例
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>
<!-- JUnit 5 用于测试 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
</dependencies>
实体类与验证注解
import jakarta.validation.constraints.*;
import jakarta.validation.Valid;
import java.time.LocalDate;
import java.util.List;
public class User {
@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 = 100, message = "密码长度至少6位")
private String password;
@Email(message = "邮箱格式不正确")
@NotBlank(message = "邮箱不能为空")
private String email;
@NotNull(message = "年龄不能为空")
@Min(value = 18, message = "年龄必须大于18岁")
@Max(value = 130, message = "年龄不能超过130岁")
private Integer age;
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
private String phoneNumber;
@Past(message = "出生日期必须是过去的日期")
private LocalDate birthDate;
@DecimalMin(value = "0.0", message = "余额不能为负数")
@DecimalMax(value = "999999.99", message = "余额超出范围")
private Double balance;
@Valid
private Address address;
@Size(min = 1, message = "至少要有一个联系电话")
@Valid
private List<Phone> phones;
// getters and setters 省略
}
class Address {
@NotBlank(message = "省份不能为空")
private String province;
@NotBlank(message = "城市不能为空")
private String city;
@NotBlank(message = "详细地址不能为空")
private String detail;
}
class Phone {
@NotBlank(message = "电话号码不能为空")
@Pattern(regexp = "^\\d{3,4}-\\d{7,8}$", message = "固定电话格式不正确")
private String number;
@NotNull(message = "电话类型不能为空")
private String type;
}
自定义验证注解
// 自定义注解 - 验证强密码
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.*;
@Documented
@Constraint(validatedBy = StrongPasswordValidator.class)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface StrongPassword {
String message() default "密码必须包含大小写字母、数字和特殊字符";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
// 验证器实现
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
public class StrongPasswordValidator implements ConstraintValidator<StrongPassword, String> {
@Override
public void initialize(StrongPassword strongPassword) {
// 初始化逻辑
}
@Override
public boolean isValid(String password, ConstraintValidatorContext context) {
if (password == null) {
return true; // 让 @NotBlank 处理空值
}
boolean hasUpper = password.chars().anyMatch(Character::isUpperCase);
boolean hasLower = password.chars().anyMatch(Character::isLowerCase);
boolean hasDigit = password.chars().anyMatch(Character::isDigit);
boolean hasSpecial = password.chars().anyMatch(ch -> !Character.isLetterOrDigit(ch));
boolean hasLength = password.length() >= 8;
return hasUpper && hasLower && hasDigit && hasSpecial && hasLength;
}
}
// 使用自定义注解
public class UserRegistration {
@NotBlank(message = "密码不能为空")
@StrongPassword
private String password;
}
验证服务
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import java.util.Set;
import java.util.stream.Collectors;
public class ValidationService {
private final Validator validator;
public ValidationService() {
// 获取验证器
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
this.validator = factory.getValidator();
}
// 基础验证方法
public <T> Set<ConstraintViolation<T>> validate(T object) {
return validator.validate(object);
}
// 批量验证并返回错误消息
public <T> List<String> validateAndGetMessages(T object) {
Set<ConstraintViolation<T>> violations = validator.validate(object);
return violations.stream()
.map(ConstraintViolation::getMessage)
.collect(Collectors.toList());
}
// 按分组验证
public <T> Set<ConstraintViolation<T>> validateByGroup(T object, Class<?> group) {
return validator.validate(object, group);
}
// 验证单个属性
public <T> Set<ConstraintViolation<T>> validateProperty(T object, String propertyName) {
return validator.validateProperty(object, propertyName);
}
}
测试案例
import jakarta.validation.ConstraintViolation;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Set;
public class UserValidationTest {
private static ValidationService validationService;
@BeforeAll
public static void setup() {
validationService = new ValidationService();
}
@Test
public void testInvalidUser() {
User user = new User();
user.setUsername("ab"); // 太短
user.setPassword("short"); // 太短
user.setEmail("invalid-email"); // 格式错误
user.setAge(15); // 太小
Set<ConstraintViolation<User>> violations = validationService.validate(user);
for (ConstraintViolation<User> violation : violations) {
System.out.println("字段: " + violation.getPropertyPath()
+ ", 错误: " + violation.getMessage());
}
assert (!violations.isEmpty());
}
@Test
public void testValidUser() {
User user = new User();
user.setUsername("testUser123");
user.setPassword("strongPass123");
user.setEmail("test@example.com");
user.setAge(25);
user.setPhoneNumber("13812345678");
user.setBalance(1000.50);
user.setBirthDate(LocalDate.of(1995, 1, 1));
// 设置地址
Address address = new Address();
address.setProvince("广东省");
address.setCity("深圳市");
address.setDetail("南山区科技园");
user.setAddress(address);
Set<ConstraintViolation<User>> violations = validationService.validate(user);
assert (violations.isEmpty());
}
@Test
public void testCustomPassword() {
UserRegistration registration = new UserRegistration();
registration.setPassword("weak");
Set<ConstraintViolation<UserRegistration>> violations = validationService.validate(registration);
for (ConstraintViolation<UserRegistration> violation : violations) {
System.out.println("错误: " + violation.getMessage());
}
assert (!violations.isEmpty());
}
}
Spring Boot Validation 案例
Spring Boot 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Controller层验证
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.validation.Valid;
import jakarta.validation.constraints.*;
import java.time.LocalDate;
@RestController
@RequestMapping("/api/users")
@Validated
public class UserController {
// 请求体验证
@PostMapping("/register")
public ApiResponse register(@Valid @RequestBody RegisterRequest request) {
// 验证通过后的业务逻辑
return ApiResponse.success("注册成功");
}
// 路径参数验证
@GetMapping("/{userId}")
public User getUser(@PathVariable @Min(1) @NotNull Long userId) {
// 业务逻辑
return userService.getUserById(userId);
}
// 查询参数验证
@GetMapping("/search")
public List<User> searchUsers(
@RequestParam @Email String email,
@RequestParam @Min(18) @Max(130) Integer age,
@RequestParam @Pattern(regexp = "^[a-zA-Z]+$") String name) {
// 业务逻辑
return userService.searchUsers(email, age, name);
}
}
// 请求DTO
public class RegisterRequest {
@NotBlank(message = "用户名不能为空")
@Size(min = 3, max = 20)
@Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "用户名格式不正确")
private String username;
@NotBlank(message = "密码不能为空")
@Size(min = 8, max = 20, message = "密码长度必须在8-20之间")
@StrongPassword
private String password;
@Email(message = "邮箱格式不正确")
@NotBlank(message = "邮箱不能为空")
private String email;
@NotNull(message = "生日不能为空")
@Past(message = "生日必须是过去的日期")
private LocalDate birthday;
@Valid
private AddressDTO address;
// getters and setters
}
全局异常处理
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import jakarta.validation.ConstraintViolationException;
import java.util.HashMap;
import java.util.Map;
@RestControllerAdvice
public class GlobalExceptionHandler {
// 处理RequestBody验证异常
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ApiResponse<Map<String, String>>> handleValidationExceptions(
MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach((error) -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
return ResponseEntity.badRequest()
.body(ApiResponse.error("验证失败", errors));
}
// 处理参数和路径变量验证异常
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<ApiResponse<Map<String, String>>> handleConstraintViolation(
ConstraintViolationException ex) {
Map<String, String> errors = new HashMap<>();
ex.getConstraintViolations().forEach(cv -> {
String path = cv.getPropertyPath().toString();
String message = cv.getMessage();
errors.put(path, message);
});
return ResponseEntity.badRequest()
.body(ApiResponse.error("参数验证失败", errors));
}
// 统一响应格式
public static class ApiResponse<T> {
private boolean success;
private String message;
private T data;
// 构造器、getters/setters
public static <T> ApiResponse<T> success(T data) {
ApiResponse<T> response = new ApiResponse<>();
response.setSuccess(true);
response.setData(data);
return response;
}
public static <T> ApiResponse<T> error(String message, T data) {
ApiResponse<T> response = new ApiResponse<>();
response.setSuccess(false);
response.setMessage(message);
response.setData(data);
return response;
}
}
}
Service层验证
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import jakarta.validation.Valid;
import jakarta.validation.constraints.*;
@Service
@Validated
public class UserService {
// 方法参数验证
public User createUser(@Valid @NotNull User user) {
return userRepository.save(user);
}
// 方法参数验证
public User updateUserEmail(
@Min(1) Long userId,
@Email @NotBlank String newEmail) {
User user = userRepository.findById(userId).orElseThrow();
user.setEmail(newEmail);
return userRepository.save(user);
}
// 返回值验证(启动后每次返回都会验证)
public @Valid User getUserById(@Min(1) Long userId) {
return userRepository.findById(userId).orElseThrow();
}
}
分组验证案例
// 定义分组接口
public interface CreateGroup {}
public interface UpdateGroup {}
// 使用分组
public class User {
@Null(groups = CreateGroup.class, message = "创建时ID必须为空")
@NotNull(groups = UpdateGroup.class, message = "更新时ID不能为空")
private Long id;
@NotBlank(message = "用户名不能为空")
private String username;
@Email
private String email;
}
性能优化和最佳实践
手动验证优化
public class OptimizedValidationService {
private static final Validator validator;
static {
// 使用缓存和初始化配置
ValidatorFactory factory = Validation.byProvider(HibernateValidator.class)
.configure()
.failFast(true) // 快速失败,只返回第一个错误
.buildValidatorFactory();
validator = factory.getValidator();
}
// 使用 @Validated 和 Spring 的依赖注入
@Autowired
public OptimizedValidationService(Validator validator) {
// 使用Spring管理的Validator实例
}
// 构建器模式创建对象时验证
public static class UserBuilder {
private User user;
public UserBuilder validateOnBuild() {
Set<ConstraintViolation<User>> violations = validator.validate(user);
if (!violations.isEmpty()) {
String errors = violations.stream()
.map(v -> v.getPropertyPath() + ": " + v.getMessage())
.collect(Collectors.joining(", "));
throw new ValidationException(errors);
}
return this;
}
}
}
自定义约束组合
// 组合多个约束
@NotBlank(message = "手机号不能为空")
@Pattern(
regexp = "^1[3-9]\\d{9}$",
message = "手机号格式不正确"
)
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {})
public @interface ChinesePhoneNumber {
String message() default "手机号不合法";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
条件验证
// 条件验证示例 - 使用自定义验证器
public class ConditionalValidator {
// 根据性别验证不同规则
public static class GenderConstraintValidator
implements ConstraintValidator<ValidPerson, Person> {
@Override
public void initialize(ValidPerson constraintAnnotation) {}
@Override
public boolean isValid(Person person, ConstraintValidatorContext context) {
if (person.getGender() == null) {
return false;
}
if ("MALE".equals(person.getGender())) {
return person.getAge() >= 22; // 男性结婚年龄
} else if ("FEMALE".equals(person.getGender())) {
return person.getAge() >= 20; // 女性结婚年龄
}
return false;
}
}
}
这个案例涵盖了Java验证框架的核心特性和实际应用场景,您可以根据项目需求选择合适的验证方式,并调整错误消息以符合业务要求。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播