Jakarta Validation 校验注解速查手册
📅 2026/7/3 3:47:04
👁️ 阅读次数
📝 编程学习
文章目录
- 1.常见注解
- 1.1空值与非空校验
- 1.2数值范围校验
- 1.3字符串与格式校验
- 1.4.布尔值校验
- 1.5日期与时间校验
- 1.6重复注解(Repeatable Annotations)
- 2.@Valid VS @Validated
- 2.1核心区别
- 2.2引入依赖
- 2.3DTO示例
- 2.4Controller示例
- 3.校验异常及相关处理
- 3.1三种校验异常及触发场景
- 3.2异常处理
jakarta.validation.constraints包提供了 Jakarta Bean Validation 规范的核心内置注解,覆盖空值检查、数值范围、字符串格式、布尔值、日期时间等常见校验场景。下面按功能分类列出常用注解及其说明。1.常见注解
1.1空值与非空校验
| 注解 | 说明 | 支持类型 |
|---|---|---|
@Null | 元素必须为null | 任意类型 |
@NotNull | 元素不能为null | 任意类型 |
@NotEmpty | 元素不能为null,且不能为空(如集合、字符串、数组等) | CharSequence、Collection、Map、数组 |
@NotBlank | 元素不能为null,且必须至少包含一个非空白字符(会 trim 空白后再检查) | CharSequence |
@NotBlank比@NotEmpty更严格,适合校验用户输入的非空字符串。
1.2数值范围校验
| 注解 | 说明 | 支持类型 |
|---|---|---|
@Min(value)/@Max(value) | 数值必须大于/等于或小于/等于指定的整数值 | BigInteger、byte、short、int、long及包装类 |
@DecimalMin(value)/@DecimalMax(value) | 数值必须大于/等于或小于/等于指定的十进制数值 | BigDecimal、CharSequence、数值类型 |
@Digits(integer,fraction) | 数值的整数位数和小数位数必须在指定范围内 | BigDecimal、BigInteger、CharSequence、数值类型 |
@Positive/@PositiveOrZero | 数值必须为正数(或正数或零) | 数值类型 |
@Negative/@NegativeOrZero | 数值必须为负数(或负数或零) | 数值类型 |
1.3字符串与格式校验
| 注解 | 说明 | 支持类型 |
|---|---|---|
@Size(min,max) | 元素的大小(如字符串长度、集合大小)必须在min和max之间 | CharSequence、Collection、Map、数组 |
@Pattern(regexp) | 字符串必须匹配指定的正则表达式 | CharSequence |
@Email | 字符串必须是一个格式合法的 Email 地址 | CharSequence |
1.4.布尔值校验
| 注解 | 说明 | 支持类型 |
|---|---|---|
@AssertTrue | 元素必须为true | boolean、Boolean |
@AssertFalse | 元素必须为false | boolean、Boolean |
null值对于@AssertTrue和@AssertFalse被视为有效。
1.5日期与时间校验
| 注解 | 说明 | 支持类型 |
|---|---|---|
@Past/@PastOrPresent | 日期必须在过去(或过去或现在) | Date、Calendar、Instant、LocalDate、LocalDateTime等 |
@Future/@FutureOrPresent | 日期必须在未来(或未来或现在) | 同上 |
1.6重复注解(Repeatable Annotations)
Jakarta Bean Validation 2.0 开始,所有内置校验注解都支持重复使用。当需要对同一个字段应用多个相同类型的约束时,可以使用其List形式,例如:
@Pattern.List({@Pattern(regexp=".*[A-Z].*",message="密码必须包含至少一个大写字母"),@Pattern(regexp=".*[a-z].*",message="密码必须包含至少一个小写字母"),@Pattern(regexp=".*\\d.*",message="密码必须包含至少一个数字")})privateStringpassword;2.@Valid VS @Validated
@Valid是Jakarta Bean Validation规范提供的注解(位于jakarta.validation.Valid包下),并非Spring 框架独有。它的核心作用是触发关联对象的级联校验,是处理嵌套对象校验的标配。@Validated是Spring 框架提供的注解(位于org.springframework.validation.annotation包下),并非Jakarta Bean Validation 规范的一部分。它的核心作用是开启 Spring 对方法参数校验的支持,并提供了分组校验的能力。
2.1核心区别
| 对比维度 | @Valid(Jakarta 标准) | @Validated(Spring 专属) |
|---|---|---|
| 所属框架 | Jakarta Bean Validation(JSR 规范) | Spring Framework |
| 核心作用 | 对象内部级联校验(验证嵌套对象) | 方法级参数校验+分组校验 |
| 使用位置 | 方法参数、字段(用于嵌套对象)、构造器 | 类、方法参数、方法(需放在类上才能激活 AOP 代理) |
| 能否指定分组 | ❌ 不能,只走Default分组 | ✅能,可通过@Validated(GroupA.class)指定 |
2.2引入依赖
Jakarta Bean Validation
引入依赖:
<!-- Hibernate Validator --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>依赖的传递链如下:
spring-boot-starter-validation └── hibernate-validator(校验实现) └── jakarta.validation-api(校验接口定义) └── jakarta.validation.constraints ← @NotNull、@NotEmpty 等注解所在- jakarta.validation-api:定义接口规范(@NotNull、@NotEmpty 等注解都在这里)。
- hibernate-validator:上述规范的具体实现者。
- spring-boot-starter-validation:Spring Boot 将两者打包在一起,项目中引入这个 starter 即可同时获得接口和实现。
Spring框架提供的校验注解
引入依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>依赖的传递链如下:
spring-boot-starter-web └── spring-webmvc └── spring-context ← @Validated 定义在此
2.3DTO示例
publicclassUserBo{@Valid//少了这个,address的内部校验不会触发privateAddressaddress;@NotBlank(message="用户名不能为空")@Size(min=2,max=20,message="用户名长度须为2-20个字符")privateStringusername;@Email(message="邮箱格式不正确")privateStringemail;@Pattern(regexp="^1[3-9]\\d{9}$",message="手机号格式不正确")privateStringphone;@Min(value=0,message="年龄不能为负数")@Max(value=150,message="年龄不能超过150岁")privateIntegerage;@Positive(message="金额必须大于0")@Digits(integer=10,fraction=2,message="金额格式不正确")privateBigDecimalamount;@Future(message="生效时间必须是未来时间")privateLocalDateTimeeffectiveTime;}publicclassAddress{@NotBlankprivateStringprovince;@NotBlankprivateStringcity;}2.4Controller示例
场景一:对@RequestParam/@PathVariable进行校验(必用@Validated)
在 Spring Controller 中,如果你需要对 URL 中的参数或路径变量做校验,必须在类上加上@Validated,否则@Min、@Max等注解不会生效。
@RestController@Validated// 必须加在类上,才能激活方法参数(如 @RequestParam)的校验publicclassUserController{@GetMapping("/user")publicStringgetUser(@RequestParam@Min(1)Longid){// 当 id < 1 时,Spring 会自动抛出 ConstraintViolationExceptionreturn"user";}@DeleteMapping("/user/{id}")publicStringdeleteUser(@PathVariable@Min(1)Longid){// 当 id < 1 时,Spring 会自动抛出 ConstraintViolationExceptionreturn"user";}}场景二:分组校验(@Validated独有的杀手锏)
@Valid无法指定校验分组,而@Validated可以。这在新增(id 为空)和更新(id 必须不为空)共用一个 DTO 时非常实用。
java
// 1. 定义分组接口publicinterfaceCreateGroup{}publicinterfaceUpdateGroup{}// 2. DTO 中使用分组publicclassUserDto{@Null(groups=CreateGroup.class)// 新增时 id 必须为空@NotNull(groups=UpdateGroup.class)// 更新时 id 必须不为空privateLongid;@NotBlank(groups={CreateGroup.class,UpdateGroup.class})privateStringname;}// 3. Controller 中使用 @Validated 指定分组@RestControllerpublicclassUserController{@PostMapping("/user")publicStringcreate(@Validated(CreateGroup.class)@RequestBodyUserDtodto){// 只校验 CreateGroup 分组下的约束(id 必须为空)return"created";}@PutMapping("/user")publicStringupdate(@Validated(UpdateGroup.class)@RequestBodyUserDtodto){// 只校验 UpdateGroup 分组下的约束(id 必须不为空)return"updated";}}3.校验异常及相关处理
3.1三种校验异常及触发场景
| 异常类 | 触发场景 |
|---|---|
MethodArgumentNotValidException | @RequestBody + @Validated 参数校验失败 |
ConstraintViolationException | 方法级参数直接校验失败(非 Body) |
BindException | 表单/Query 参数绑定校验失败 |
//1.MethodArgumentNotValidException@PostMapping()publicR<Void>add(@Validated(AddGroup.class)@RequestBodySecIllegalDrivingBobo){returntoAjax(secIllegalDrivingService.insertByBo(bo));}//2.ConstraintViolationException@GetMapping("/{id}")publicR<SecIllegalDrivingVo>getInfo(@NotNull(message="主键不能为空")@PathVariableLongid){returnR.ok(secIllegalDrivingService.queryById(id));}//3.BindException@GetMapping("/list")publicTableDataInfo<SecIllegalDrivingVo>list(SecIllegalDrivingBobo,PageQuerypageQuery){returnsecIllegalDrivingService.queryPageList(bo,pageQuery);}3.2异常处理
每种异常的处理都遵循 “记录日志 + 提取 message + 统一返回” 三步模式。
MethodArgumentNotValidException(Body 校验)
@ExceptionHandler(MethodArgumentNotValidException.class)publicR<Void>handleMethodArgumentNotValidException(MethodArgumentNotValidExceptione){//1.写入日志log.error(e.getMessage());//2.提取所有字段的 message,用 ", " 拼接Stringmessage=StreamUtils.join(e.getBindingResult().getAllErrors(),DefaultMessageSourceResolvable::getDefaultMessage,", ");//3.统一响应返回前端returnR.fail(message);}ConstraintViolationException(方法参数校验)
@ExceptionHandler(ConstraintViolationException.class)publicR<Void>constraintViolationException(ConstraintViolationExceptione){//1.写入日志log.error(e.getMessage());//2.从 ConstraintViolation 集合中提取 getMessage(),用 ", " 拼接Stringmessage=StreamUtils.join(e.getConstraintViolations(),ConstraintViolation::getMessage,", ");//3.统一响应返回前端returnR.fail(message);}BindException(表单绑定校验)
@ExceptionHandler(BindException.class)publicR<Void>handleBindException(BindExceptione){//1.写入日志log.error(e.getMessage());//2.获取并拼接异常信息Stringmessage=StreamUtils.join(e.getAllErrors(),DefaultMessageSourceResolvable::getDefaultMessage,", ");//3.统一响应返回前端returnR.fail(message);}
编程学习
技术分享
实战经验