关于 javax.validation.constraints的详细说明

以下是关于 javax.validation.constraints(现为 ​Jakarta Bean Validation)的详细说明,涵盖核心注解、使用场景、代码示例及最佳实践:


一、javax.validation.constraints 是什么?

  • 作用:提供一组标准注解,用于对 Java Bean 的字段或方法参数进行数据校验(如非空、长度、范围等)。
  • 规范演进
    • Java EE 时期:包名为 javax.validation.constraints
    • Jakarta EE 9+:包名迁移为 jakarta.validation.constraints(需注意依赖兼容性)。

二、核心注解列表及用法

1. 常用注解
注解 校验规则 支持类型
@NotNull 值不能为 null 任意类型
@NotBlank 字符串不能为空或纯空格 String
@NotEmpty 集合/数组/字符串不能为空(长度 > 0) Collection, String
@Size(min, max) 元素数量或字符串长度在指定范围内 集合、数组、字符串
@Min(value) 数值必须 ≥ 指定最小值 数值类型(int, long 等)
@Max(value) 数值必须 ≤ 指定最大值 同上
@DecimalMin(value) 数值必须 ≥ 指定最小值(字符串形式,支持精度) BigDecimal, String
@DecimalMax(value) 数值必须 ≤ 指定最大值(字符串形式,支持精度) 同上
@Digits(integer, fraction) 数值整数部分最多 integer 位,小数部分最多 fraction 数值类型
@Pattern(regexp) 字符串必须匹配正则表达式 String
@Email 字符串必须是合法邮箱格式 String
@Positive / @PositiveOrZero 数值必须为正数或零 数值类型
@Negative / @NegativeOrZero 数值必须为负数或零 数值类型
@Future / @FutureOrPresent 日期必须在未来(或包含当前) Date, LocalDate
@Past / @PastOrPresent 日期必须在过去(或包含当前) 同上
2. 注解示例代码
复制代码
public class User {
    @NotBlank(message = "用户名不能为空")
    private String username;

    @Email(message = "邮箱格式无效")
    private String email;

    @Size(min = 6, max = 20, message = "密码长度需在6-20位之间")
    private String password;

    @Min(value = 18, message = "年龄必须≥18岁")
    @Max(value = 100, message = "年龄必须≤100岁")
    private Integer age;

    @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式无效")
    private String phone;
}

三、集成到 Spring Boot 中的步骤

1. 添加依赖
复制代码
<!-- Spring Boot 2.x 使用 javax.validation -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

<!-- Jakarta EE 9+ 使用 jakarta.validation -->
<dependency>
    <groupId>jakarta.validation</groupId>
    <artifactId>jakarta.validation-api</artifactId>
    <version>3.0.2</version>
</dependency>
2. 在 Controller 中触发校验

使用 @Valid@Validated 注解触发校验:

复制代码
@PostMapping("/users")
public ResponseEntity<?> createUser(@RequestBody @Valid User user) {
    // 校验通过后执行业务逻辑
    return ResponseEntity.ok("用户创建成功");
}
3. 处理校验异常

通过 @ExceptionHandler 捕获 MethodArgumentNotValidException

复制代码
@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Map<String, String>> handleValidationException(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(errors);
    }
}

四、高级用法

1. 分组校验

通过分组接口实现不同场景下的差异化校验:

复制代码
// 定义分组接口
public interface CreateGroup {}
public interface UpdateGroup {}

public class User {
    @NotNull(groups = UpdateGroup.class)
    private Long id;

    @NotBlank(groups = {CreateGroup.class, UpdateGroup.class})
    private String name;
}

// 在 Controller 中指定分组
@PostMapping("/users")
public ResponseEntity<?> createUser(@RequestBody @Validated(CreateGroup.class) User user) { ... }
2. 自定义校验注解

实现自定义校验逻辑(如密码强度校验):

复制代码
@Target({FIELD})
@Retention(RUNTIME)
@Constraint(validatedBy = PasswordValidator.class)
public @interface StrongPassword {
    String message() default "密码必须包含大小写字母和数字";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

public class PasswordValidator implements ConstraintValidator<StrongPassword, String> {
    @Override
    public boolean isValid(String password, ConstraintValidatorContext context) {
        return password.matches("^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).+$");
    }
}
3. 级联校验

校验对象内的嵌套对象:

复制代码
public class Order {
    @Valid
    private List<@Valid Product> products; // 校验每个 Product 的字段
}

五、校验失败的错误消息定制

1. 默认消息模板

每个注解的 message 属性支持占位符:

复制代码
@Size(min = 6, max = 20, message = "密码长度需在{min}-{max}位之间")
private String password;
2. 国际化消息

messages.propertiesValidationMessages.properties 中定义:

复制代码
user.email.invalid=邮箱格式无效

注解中使用:

复制代码
@Email(message = "{user.email.invalid}")
private String email;

六、常见问题与解决方案

问题 解决方案
校验未生效 检查是否添加了 @Valid@Validated 注解;确认依赖已正确引入
嵌套对象校验失败 在嵌套对象字段上添加 @Valid 注解
分组校验不生效 @Validated 注解中明确指定分组接口
自定义校验器未触发 确认 @Constraint(validatedBy = MyValidator.class) 并实现 ConstraintValidator

七、总结

  • 核心价值:通过声明式注解简化数据校验逻辑,减少样板代码。
  • 最佳实践
    • 优先使用标准注解,避免重复造轮子。
    • 结合分组校验实现多场景复用。
    • 统一处理校验异常,返回清晰的错误信息。
  • 扩展性:通过自定义注解和校验器满足复杂业务需求。
相关推荐
0xDevNull1 小时前
MySQL数据冷热分离详解
后端·mysql
科技小花2 小时前
数据治理平台架构演进观察:AI原生设计如何重构企业数据管理范式
数据库·重构·架构·数据治理·ai-native·ai原生
一江寒逸2 小时前
零基础从入门到精通MySQL(中篇):进阶篇——吃透多表查询、事务核心与高级特性,搞定复杂业务SQL
数据库·sql·mysql
D4c-lovetrain2 小时前
linux个人心得22 (mysql)
数据库·mysql
阿里小阿希2 小时前
CentOS7 PostgreSQL 9.2 升级到 15 完整教程
数据库·postgresql
荒川之神2 小时前
Oracle 数据仓库雪花模型设计(完整实战方案)
数据库·数据仓库·oracle
做个文艺程序员3 小时前
MySQL安全加固十大硬核操作
数据库·mysql·安全
不吃香菜学java3 小时前
Redis简单应用
数据库·spring boot·tomcat·maven
一个天蝎座 白勺 程序猿3 小时前
Apache IoTDB(15):IoTDB查询写回(INTO子句)深度解析——从语法到实战的ETL全链路指南
数据库·apache·etl·iotdb
不知名的老吴3 小时前
Redis的延迟瓶颈:TCP栈开销无法避免
数据库·redis·缓存