开发者

Spring Validation中9个数据校验工具使用指南

开发者 https://www.devze.com 2025-05-12 10:18 出处:网络 作者: 风象南
目录1. Bean Validation基础注解常用注解示例在控制器中应用2. 自定义约束验证器定义自定义约束注解实现验证器应用自定义约束3. 分组验证定义验证分组应用分组到约束在控制器中指定分组4. 嵌套验证定义嵌套对象5. 方
目录
  • 1. Bean Validation基础注解
    • 常用注解示例
    • 在控制器中应用
  • 2. 自定义约束验证器
    • 定义自定义约束注解
    • 实现验证器
    • 应用自定义约束
  • 3. 分组验证
    • 定义验证分组
    • 应用分组到约束
    • 在控制器中指定分组
  • 4. 嵌套验证
    • 定义嵌套对象
  • 5. 方法级别验证
    • 启用方法级别验证
    • 定义带验证的服务方法
  • 6. 错误消息处理和国际化
    • 自定义错误消息
    • 国际化错误消息
    • 使用自定义消息
  • 7. 程序化验证
    • 使用Validator手动验证对象
  • 8. 组合约束
    • 创建组合约束
    • 应用组合约束
  • 9. 跨字段验证
    • 创建类级别约束
    • 实现验证器
    • 应用类级别约束
  • 总结

    1. Bean Validation基础注解

    Spring Validation集成了jsR-380 (Bean Validation 2.0)规范,提供了一系列开箱即用的校验注解。

    常用注解示例

    @Data
    public class UserDTO {
        @NotNull(message = "用户ID不能为空")
        private Long id;
        
        @NotBlank(message = "用户名不能为空")
        @Size(min = 4, max = 20, message = "用户名长度必须在4到20个字符之间")
        private String username;
        
        @Email(message = "邮箱格式不正确")
        private String email;
        
        @Min(value = 18, message = "年龄必须大于或等于18")
        @Max(value = 120, message = "年龄必须小于或等于120")
        private Integer age;
        
        @Past(message = "出生日期必须是过去的日期")
        private LocalDate birthDate;
        
        @Pattern(regexp = "^1[3-9]\d{9}$", message = "手机号码格式不正确")
        prpythonivate String phoneNumber;
    }
    

    在控制器中应用

    @RestController
    @RequestMapping("/api/users")
    public class UserController {
        
        @PostMapping
        public ResponseEntity<UserDTO> createUser(@RequestBody @Valid UserDTO userDTO, 
                                                 BindingResult bindingResult) {
            if (bindingResult.hasErrors()) {
                // 处理验证错误
                throw new ValidationException(bindingResult);
            }
            // 处理业务逻辑
            return ResponseEntity.ok(userDTO);
        }
    }
    

    最佳实践:使用有意义的错误消息,保持一致的命名风格,避免在实体类上直接使用验证注解,而是在DTO对象上应用验证规则。

    2. 自定义约束验证器

    Spring Validation允许开发者创建自定义约束,满足特定业务规则的验证需求。

    定义自定义约束注解

    @Target({ElementType.FIELD, ElementType.PARAMETER})
    @Retention(RetentionPolicy.RUNTIME)
    @Constraint(validatedBy = UniqueUsernawww.devze.commeValidator.class)
    public @interface UniqueUsername {
        String message() default "用户名已存在";
        Class<?>[] groups() default {};
        Class<? extends Payload>[] payload() default {};
    }
    

    实现验证器

    public class UniqueUsernameValidator implements ConstraintValidator<UniqueUsername, String> {
        
        @Autowired
        private UserRepository userRepository;
        
        @Override
        public boolean isValid(String username, ConstraintValidatorContext context) {
            if (username == null) {
                return true;  // 让@NotNull处理空值
            }
            return !userRepository.existsByUsername(username);
        }
    }
    

    应用自定义约束

    public class UserRegistrationDTO {
        
        @NotBlank
        @Size(min = 4, max = 20)
        @UniqueUsername
        private String username;
        
        // 其他字段...
    }
    

    使用场景:验证业务特定规则,如唯一性约束、密码复杂度、信用卡格式等。

    3. 分组验证

    分组验证允许根据不同场景应用不同的验证规则,例如创建和更新操作可能需要不同的验证逻辑。

    定义验证分组

    // 定义验证分组接口
    public interface ValidationGroups {
        interface Create {}
        interface Update {}
    }
    

    应用分组到约束

    @Data
    public class ProductDTO {
        
        @Null(groups = ValidationGroups.Create.class, message = "创建产品时ID必须为空")
        @NotNull(groups = ValidationGroups.Update.class, message = "更新产品时ID不能为空")
        private Long id;
        
        @NotBlank(groups = {ValidationGroups.Create.class, ValidationGroups.Update.class})
        private String name;
        
        @PositiveOrZero(groups = ValidationGroups.Create.class)
        @Positive(groups = ValidationGroups.Update.class)
        private BigDecimal price;
    }
    

    在控制器中指定分组

    @RestController
    @RequestMapping("/api/products")
    public class ProductController {
        
        @PostMapping
        public ResponseEntity<ProductDTO> createProduct(
                @RequestBody @Validated(ValidationGroups.Create.class) ProductDTO productDTO) {
            // 创建产品逻辑
            return ResponseEntity.ok(productDTO);
        }
        
        @PutMapping("/{id}")
        public ResponseEntity<ProductDTO> updateProduct(
                @PathVariable Long id,
                @RequestBody @Validated(ValidationGroups.Update.class) ProductDTO productDTO) {
            // 更新产品逻辑
            return ResponseEntity.ok(productDTO);
        }
    }
    

    提示:注意使用@Validatandroided注解而不是@Valid,因为只有前者支持分组验证。

    4. 嵌套验证

    嵌套验证允许验证复杂对象结构中的嵌套对象。

    定义嵌套对象

    @Data
    public class OrderDTO {
        
        @NotNull
        private Long id;
        
        @NotNull
        @Valid  // 标记需要级联验证的字段
        private CustomerDTO customer;
        
        @NotEmpty
     编程客栈   @Valid  // 验证集合中的每个元素
        private List<OrderItemDTO> items;
    }
    
    @Data
    public class CustomerDTO {
        
        @NotNull
        private Long id;
        
        @NotBlank
        private String name;
        
        @Email
        private String email;
        
        @Valid  // 进一步嵌套验证
        private AddressDTO address;
    }
    

    关键点:在需要级联验证的字段上添加@Valid注解,确保验证深入到嵌套对象中。

    5. 方法级别验证

    Spring Validation不仅可以用于控制器参数,还可以应用于服务层的方法。

    启用方法级别验证

    @Configuration
    @EnableMethodValidation
    public class ValidationConfig {
        // 配置内容
    }
    

    定义带验证的服务方法

    @Service
    public class UserService {
      编程客栈  
        @Validated
        public User createUser(@Valid UserDTO userDTO) {
            // 业务逻辑
            return new User();
        }
        
        @NotNull
        public User findById(@Min(1) Long id) {
            // 查询逻辑
            return new User();
        }
        
        @Validated(ValidationGroups.Update.class)
        public void updateUser(@Valid UserDTO userDTO) {
            // 更新逻辑
        }
    }
    

    应用场景:确保服务层方法接收到的参数和返回的结果符合预期,增强代码的健壮性。

    6. 错误消息处理和国际化

    Spring Validation提供了强大的错误消息处理和国际化支持。

    自定义错误消息

    ValidationMessages.properties文件中定义:

    # ValidationMessages.properties
    Javax.validation.constraints.NotEmpty.message=字段不能为空
    javax.validation.constraints.Email.message=不是有效的电子邮箱地址
    user.name.size=用户名长度必须在{min}到{max}个字符之间
    

    国际化错误消息

    创建特定语言的属性文件:

    # ValidationMessages_en.properties
    javax.validation.constraints.NotEmpty.message=Field cannot be empty
    javax.validation.constraints.Email.message=Not a valid email address
    user.name.size=Username must be between {min} and {max} characters
    
    # ValidationMessages_zh_CN.properties
    javax.validation.constraints.NotEmpty.message=字段不能为空
    javax.validation.constraints.Email.message=不是有效的电子邮箱地址
    user.name.size=用户名长度必须在{min}到{max}个字符之间
    

    使用自定义消息

    @Size(min = 4, max = 20, message = "{user.name.size}")
    private String username;
    

    7. 程序化验证

    除了注解驱动的验证,Spring Validation还支持以编程方式进行验证。

    使用Validator手动验证对象

    @Service
    public class ValidationService {
        
        private final Validator validator;
    
        public ValidationService(Validator validator) {
            this.validator = validator;
        }
        
        public <T> void validate(T object) {
            Set<ConstraintViolation<T>> violations = validator.validate(object);
            if (!violations.isEmpty()) {
                throw new ConstraintViolationException(violations);
            }
        }
        
        public <T> void validateWithGroup(T object, Class<?>... groups) {
            Set<ConstraintViolation<T>> violations = validator.validate(object, groups);
            if (!violations.isEmpty()) {
                throw new ConstraintViolationException(violations);
            }
        }
        
        public <T> List<String> getValidationErrors(T object) {
            return validator.validate(object).stream()
                    .map(ConstraintViolation::getMessage)
                    .collect(Collectors.toList());
        }
    }
    

    使用场景:在复杂业务逻辑中需要条件性验证,或者验证非控制器传入的对象时。

    8. 组合约束

    组合约束允许将多个基本约束组合成一个更复杂的约束,减少代码重复。

    创建组合约束

    @NotNull
    @Size(min = 8, max = 30)
    @Pattern(regexp = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=]).*$", 
             message = "密码必须包含至少一个数字、小写字母、大写字母和特殊字符")
    @Target({ElementType.FIELD, ElementType.PARAMETER})
    @Retention(RetentionPolicy.RUNTIME)
    @Constraint(validatedBy = {})
    public @interface StrongPassword {
        String message() default "密码不符合安全要求";
        Class<?>[] groups() default {};
        Class<? extends Payload>[] payload() default {};
    }
    

    应用组合约束

    public class PasswordChangeDTO {
        
        @NotBlank
        private String oldPassword;
        
        @StrongPassword
        private String newpassword;
        
        @NotBlank
        private String confirmPassword;
    }
    

    优点:提高代码可读性和可维护性,确保验证规则在整个应用中保持一致。

    9. 跨字段验证

    跨字段验证允许根据多个字段之间的关系进行验证。

    创建类级别约束

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Constraint(validatedBy = PasswordMatchesValidator.class)
    public @interface PasswordMatches {
        String message() default "确认密码与新密码不匹配";
        Class<?>[] groups() default {};
        Class<? extends Payload>[] payload() default {};
        
        String field();
        String fieldMatch();
    }
    

    实现验证器

    public class PasswordMatchesValidator implements ConstraintValidator<PasswordMatches, Object> {
        
        private String field;
        private String fieldMatch;
        
        @Override
        public void initialize(PasswordMatches constraintAnnotation) {
            this.field = constraintAnnotation.field();
            this.fieldMatch = constraintAnnotation.fieldMatch();
        }
        
        @Override
        public boolean isValid(Object value, ConstraintValidatorContext context) {
            try {
                Object fieldValue = BeanUtils.getPropertyDescriptor(value.getClass(), field)
                        .getReadMethod().invoke(value);
                Object fieldMatchValue = BeanUtils.getPropertyDescriptor(value.getClass(), fieldMatch)
                        .getReadMethod().invoke(value);
                
                return (fieldValue != null) && fieldValue.equals(fieldMatchValue);
            } catch (Exception e) {
                return false;
            }
        }
    }
    

    应用类级别约束

    @Data
    @PasswordMatches(field = "newPassword", fieldMatch = "confirmPassword")
    public class PasswordChangeDTO {
        
        @NotBlank
        private String oldPassword;
        
        @StrongPassword
        private String newPassword;
        
        @NotBlank
        private String confirmPassword;
    }
    

    使用场景:验证密码确认、日期范围比较、最小/最大值比较等。

    总结

    Spring Validation提供了一套全面而强大的数据校验工具,从基本的注解验证到复杂的自定义约束,从单一字段验证到跨字段关系验证,都有相应的解决方案。

    合理利用这些验证工具,不仅能提高应用的健壮性和安全性,还能改善代码质量和可维护性。

    以上就是Spring Validation中9个数据校验工具使用指南的详细内容,更多关于Spring Validation数据校验工具的资料请关注编程客栈(www.devze.com)其它相关文章!

    0

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    关注公众号