数据校验(Validation) 常用的数据校验注解及其作用

【投稿赢 iPhone 17】「我的第一个开源项目」故事征集:用代码换C位出道! 10w+人浏览 1.6k人参与

在 Spring Boot 项目中,数据校验(Validation) 是保证接口输入合法、提升系统健壮性的关键环节。Java 的 Bean Validation(如 jakarta.validation)提供了一系列常用的注解,结合 @Valid@Validated 可以实现自动校验。


✅ 一、常用 Validation 注解一览

注解作用适用类型
@NotNull不能为 null任意对象
@NotBlank不能为 null 或 空白字符串(含空格)字符串
@NotEmpty不能为 null 或 空(集合、数组、字符串等)集合、数组、字符串
@Size限制大小(长度、元素个数)字符串、集合、数组
@Min / @Max数值最小/最大值数值类型
@DecimalMin / @DecimalMax小数最小/最大值(支持字符串)BigDecimal、Double 等
@Email邮箱格式校验字符串
@Pattern正则表达式匹配字符串
@Future / @Past时间必须是将来/过去日期类型
@Positive / @Negative正数/负数数值
@PositiveOrZero / @NegativeOrZero非负/非正数值

✅ 二、详细使用示例(含中文注释)

示例实体类:用户注册信息 UserRegisterDTO

package com.example.demo.dto;

import jakarta.validation.constraints.*;
import java.math.BigDecimal;
import java.time.LocalDate;

/**
 * 用户注册信息 DTO(数据传输对象)
 * 用于接收前端注册请求参数,并进行自动校验
 */
@Getter
@Setter
public class UserRegisterDTO {

    // ==================== 字符串类校验 ====================

    /**
     * 用户名不能为空,且长度在 3-20 之间
     * @NotBlank 用于字符串,自动 trim 后判断是否为空
     */
    @NotBlank(message = "用户名不能为空")
    @Size(min = 3, max = 20, message = "用户名长度必须在 {min} 到 {max} 个字符之间")
    private String username;

    /**
     * 邮箱格式必须正确,且可为空(非必填)
     * @Email 校验邮箱格式(如 user@example.com)
     */
    @Email(message = "邮箱格式不正确", regexp = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$")
    private String email;

    /**
     * 手机号必须符合中国大陆手机号格式
     * 使用正则表达式校验
     */
    @NotBlank(message = "手机号不能为空")
    @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
    private String phone;

    // ==================== 数值类校验 ====================

    /**
     * 年龄必须在 18 到 120 之间
     * @Min 和 @Max 适用于整数类型
     */
    @Min(value = 18, message = "年龄不能小于 {value}")
    @Max(value = 120, message = "年龄不能大于 {value}")
    private Integer age;

    /**
     * 账户余额必须大于 0
     * @Positive 表示严格大于 0
     */
    @Positive(message = "账户余额必须为正数")
    private BigDecimal balance;

    /**
     * 折扣率范围 0.0 到 1.0(含边界)
     * @DecimalMin/@DecimalMax 支持小数比较
     */
    @DecimalMin(value = "0.0", inclusive = true, message = "折扣率不能小于 {value}")
    @DecimalMax(value = "1.0", inclusive = true, message = "折扣率不能大于 {value}")
    private Double discount;

    // ==================== 集合与数组类校验 ====================

    /**
     * 兴趣爱好列表不能为空,且至少选 1 项,最多 5 项
     * @NotEmpty 确保集合不为 null 且不为空
     * @Size 限制集合大小
     */
    @NotEmpty(message = "兴趣爱好不能为空")
    @Size(min = 1, max = 5, message = "兴趣爱好数量必须在 {min} 到 {max} 之间")
    private List<String> hobbies;

    /**
     * 用户标签数组,最多允许 3 个
     */
    @Size(max = 3, message = "标签数量不能超过 {max} 个")
    private String[] tags;

    // ==================== 时间类校验 ====================

    /**
     * 生日必须是过去的时间
     * @Past 表示日期必须在过去(不包含现在)
     */
    @Past(message = "生日必须是过去的时间")
    private LocalDate birthday;

    /**
     * 会员到期时间必须是将来的时间
     * @Future 表示日期必须在将来
     */
    @Future(message = "会员到期时间必须是将来的时间")
    private LocalDate membershipExpiryDate;

    // ==================== 对象引用校验 ====================

    /**
     * 地址信息为嵌套对象,需使用 @Valid 启用级联校验
     */
    @Valid
    private AddressDTO address;

    // ==================== Getters and Setters ====================
}

嵌套对象示例:AddressDTO

package com.example.demo.dto;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;

/**
 * 地址信息 DTO
 * 用于级联校验(被 @Valid 引用)
 */
@Getter
@Setter
public class AddressDTO {

    @NotBlank(message = "省不能为空")
    private String province;

    @NotBlank(message = "市不能为空")
    private String city;

    @NotBlank(message = "详细地址不能为空")
    @Size(max = 200, message = "详细地址不能超过 {max} 个字符")
    private String detail;

    // Getters and Setters
}

✅ 三、Controller 使用示例

@RestController
@RequestMapping("/api/register")
public class UserController {

    @PostMapping
    public ResponseEntity<String> register(@Valid @RequestBody UserRegisterDTO dto,
                                          BindingResult result) {
        if (result.hasErrors()) {
            // 获取所有错误信息(实际项目中建议使用全局异常处理)
            String errorMsg = result.getAllErrors().stream()
                .map(e -> e.getDefaultMessage())
                .collect(Collectors.joining(", "));
            return ResponseEntity.badRequest().body("注册失败:" + errorMsg);
        }
        return ResponseEntity.ok("注册成功!");
    }
}

✅ 实际项目中建议使用 全局异常处理器 捕获 MethodArgumentNotValidException,避免每个接口都写 BindingResult


✅ 四、各注解使用场景总结

注解最佳使用场景
@NotBlank用户名、密码、昵称等非空字符串
@NotNull包装类字段(如 Long、Integer)非空判断
@NotEmpty集合、数组、字符串必须有内容
@Size限制字符串长度、集合大小、数组长度
@Min/@Max年龄、数量、页码等整数范围控制
@DecimalMin/@DecimalMax金额、折扣、税率等小数范围
@Email邮箱字段校验
@Pattern手机号、身份证、验证码等格式校验
@Future/@Past生日、有效期、预约时间等时间约束
@Positive金额、数量等必须为正
@Valid嵌套对象的级联校验

✅ 五、注意事项

  1. 仅对对象类型有效@NotBlank@Size 等对 String 有效,对 int 无效。
  2. 基本类型默认值问题:避免使用 intlong,建议用 IntegerLong,否则 @NotNull 无法生效。
  3. 配合 @Validated 使用分组校验:可实现“新增”和“更新”不同校验规则。
  4. 国际化支持message = "{key}" 可从 messages.properties 加载多语言提示。
  5. 性能影响小:校验在请求解析后、进入方法前完成,开销极小。

✅ 六、测试示例(JSON 请求)

POST /api/register
Content-Type: application/json

{
  "username": "ab",
  "email": "invalid-email",
  "phone": "12345678901",
  "age": 15,
  "balance": -100,
  "discount": 1.5,
  "hobbies": [],
  "tags": ["tag1", "tag2", "tag3", "tag4"],
  "birthday": "2025-01-01",
  "membershipExpiryDate": "2024-01-01",
  "address": {
    "province": "",
    "city": "北京",
    "detail": ""
  }
}

返回错误示例:

注册失败:用户名长度必须在 3 到 20 个字符之间, 邮箱格式不正确, 手机号格式不正确, 年龄不能小于 18, 账户余额必须为正数, 折扣率不能大于 1.0, 兴趣爱好不能为空, 标签数量不能超过 3 个, 生日必须是过去的时间, 会员到期时间必须是将来的时间, 省不能为空, 详细地址不能为空

✅ 总结

这些注解是 Spring Boot 开发中 最常用、最实用的数据校验工具,合理使用可以:

  • ✅ 减少手动 if 判断
  • ✅ 提升代码可读性
  • ✅ 实现前后端协同校验
  • ✅ 支持国际化和统一异常处理

建议在项目中结合 全局异常处理 + 国际化 + @Valid 构建完整的输入校验体系。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龙茶清欢

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值