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

2025-05-11 14:50

本文主要是介绍Spring Validation中9个数据校验工具使用指南,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《SpringValidation中9个数据校验工具使用指南》SpringValidation作为Spring生态系统的重要组成部分,提供了一套强大而灵活的数据校验机制,本文给大家介绍了Spring...

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.chinasem.cnmeValidator.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
 China编程   @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 {
  China编程  
    @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数据校验工具的资料请关注China编程(www.chinasem.cn)其它相关文章!

这篇关于Spring Validation中9个数据校验工具使用指南的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1154565

相关文章

SpringBoot整合liteflow的详细过程

《SpringBoot整合liteflow的详细过程》:本文主要介绍SpringBoot整合liteflow的详细过程,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋...  liteflow 是什么? 能做什么?总之一句话:能帮你规范写代码逻辑 ,编排并解耦业务逻辑,代码

JavaSE正则表达式用法总结大全

《JavaSE正则表达式用法总结大全》正则表达式就是由一些特定的字符组成,代表的是一个规则,:本文主要介绍JavaSE正则表达式用法的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录常用的正则表达式匹配符正则表China编程达式常用的类Pattern类Matcher类PatternSynta

Spring Security中用户名和密码的验证完整流程

《SpringSecurity中用户名和密码的验证完整流程》本文给大家介绍SpringSecurity中用户名和密码的验证完整流程,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定... 首先创建了一个UsernamePasswordAuthenticationTChina编程oken对象,这是S

java实现docker镜像上传到harbor仓库的方式

《java实现docker镜像上传到harbor仓库的方式》:本文主要介绍java实现docker镜像上传到harbor仓库的方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录1. 前 言2. 编写工具类2.1 引入依赖包2.2 使用当前服务器的docker环境推送镜像2.2

Java easyExcel实现导入多sheet的Excel

《JavaeasyExcel实现导入多sheet的Excel》这篇文章主要为大家详细介绍了如何使用JavaeasyExcel实现导入多sheet的Excel,文中的示例代码讲解详细,感兴趣的小伙伴可... 目录1.官网2.Excel样式3.代码1.官网easyExcel官网2.Excel样式3.代码

Java MQTT实战应用

《JavaMQTT实战应用》本文详解MQTT协议,涵盖其发布/订阅机制、低功耗高效特性、三种服务质量等级(QoS0/1/2),以及客户端、代理、主题的核心概念,最后提供Linux部署教程、Sprin... 目录一、MQTT协议二、MQTT优点三、三种服务质量等级四、客户端、代理、主题1. 客户端(Clien

Java中调用数据库存储过程的示例代码

《Java中调用数据库存储过程的示例代码》本文介绍Java通过JDBC调用数据库存储过程的方法,涵盖参数类型、执行步骤及数据库差异,需注意异常处理与资源管理,以优化性能并实现复杂业务逻辑,感兴趣的朋友... 目录一、存储过程概述二、Java调用存储过程的基本javascript步骤三、Java调用存储过程示

MyBatisPlus如何优化千万级数据的CRUD

《MyBatisPlus如何优化千万级数据的CRUD》最近负责的一个项目,数据库表量级破千万,每次执行CRUD都像走钢丝,稍有不慎就引起数据库报警,本文就结合这个项目的实战经验,聊聊MyBatisPl... 目录背景一、MyBATis Plus 简介二、千万级数据的挑战三、优化 CRUD 的关键策略1. 查

python实现对数据公钥加密与私钥解密

《python实现对数据公钥加密与私钥解密》这篇文章主要为大家详细介绍了如何使用python实现对数据公钥加密与私钥解密,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录公钥私钥的生成使用公钥加密使用私钥解密公钥私钥的生成这一部分,使用python生成公钥与私钥,然后保存在两个文

mysql中的数据目录用法及说明

《mysql中的数据目录用法及说明》:本文主要介绍mysql中的数据目录用法及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、背景2、版本3、数据目录4、总结1、背景安装mysql之后,在安装目录下会有一个data目录,我们创建的数据库、创建的表、插入的