Jakarta Validation 校验注解速查手册

📅 2026/7/3 3:37:17
Jakarta Validation 校验注解速查手册
文章目录1.常见注解1.1空值与非空校验1.2数值范围校验1.3字符串与格式校验1.4.布尔值校验1.5日期与时间校验1.6重复注解Repeatable Annotations2.Valid VS Validated2.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 空白后再检查CharSequenceNotBlank比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)字符串必须匹配指定的正则表达式CharSequenceEmail字符串必须是一个格式合法的 Email 地址CharSequence1.4.布尔值校验注解说明支持类型AssertTrue元素必须为trueboolean、BooleanAssertFalse元素必须为falseboolean、Booleannull值对于AssertTrue和AssertFalse被视为有效。1.5日期与时间校验注解说明支持类型Past/PastOrPresent日期必须在过去或过去或现在Date、Calendar、Instant、LocalDate、LocalDateTime等Future/FutureOrPresent日期必须在未来或未来或现在同上1.6重复注解Repeatable AnnotationsJakarta 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 ValidatedValid是Jakarta Bean Validation规范提供的注解位于jakarta.validation.Valid包下并非Spring 框架独有。它的核心作用是触发关联对象的级联校验是处理嵌套对象校验的标配。Validated是Spring 框架提供的注解位于org.springframework.validation.annotation包下并非Jakarta Bean Validation 规范的一部分。它的核心作用是开启 Spring 对方法参数校验的支持并提供了分组校验的能力。2.1核心区别对比维度ValidJakarta 标准ValidatedSpring 专属所属框架Jakarta Bean ValidationJSR 规范Spring Framework核心作用对象内部级联校验验证嵌套对象方法级参数校验分组校验使用位置方法参数、字段用于嵌套对象、构造器类、方法参数、方法需放在类上才能激活 AOP 代理能否指定分组❌ 不能只走Default分组✅能可通过Validated(GroupA.class)指定2.2引入依赖Jakarta Bean Validation引入依赖!-- Hibernate Validator -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-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-validationSpring Boot 将两者打包在一起项目中引入这个 starter 即可同时获得接口和实现。Spring框架提供的校验注解引入依赖dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependency依赖的传递链如下spring-boot-starter-web └── spring-webmvc └── spring-context ← Validated 定义在此2.3DTO示例publicclassUserBo{Valid//少了这个address的内部校验不会触发privateAddressaddress;NotBlank(message用户名不能为空)Size(min2,max20,message用户名长度须为2-20个字符)privateStringusername;Email(message邮箱格式不正确)privateStringemail;Pattern(regexp^1[3-9]\\d{9}$,message手机号格式不正确)privateStringphone;Min(value0,message年龄不能为负数)Max(value150,message年龄不能超过150岁)privateIntegerage;Positive(message金额必须大于0)Digits(integer10,fraction2,message金额格式不正确)privateBigDecimalamount;Future(message生效时间必须是未来时间)privateLocalDateTimeeffectiveTime;}publicclassAddress{NotBlankprivateStringprovince;NotBlankprivateStringcity;}2.4Controller示例场景一对RequestParam/PathVariable进行校验必用Validated在 Spring Controller 中如果你需要对 URL 中的参数或路径变量做校验必须在类上加上Validated否则Min、Max等注解不会生效。RestControllerValidated// 必须加在类上才能激活方法参数如 RequestParam的校验publicclassUserController{GetMapping(/user)publicStringgetUser(RequestParamMin(1)Longid){// 当 id 1 时Spring 会自动抛出 ConstraintViolationExceptionreturnuser;}DeleteMapping(/user/{id})publicStringdeleteUser(PathVariableMin(1)Longid){// 当 id 1 时Spring 会自动抛出 ConstraintViolationExceptionreturnuser;}}场景二分组校验Validated独有的杀手锏Valid无法指定校验分组而Validated可以。这在新增id 为空和更新id 必须不为空共用一个 DTO 时非常实用。java// 1. 定义分组接口publicinterfaceCreateGroup{}publicinterfaceUpdateGroup{}// 2. DTO 中使用分组publicclassUserDto{Null(groupsCreateGroup.class)// 新增时 id 必须为空NotNull(groupsUpdateGroup.class)// 更新时 id 必须不为空privateLongid;NotBlank(groups{CreateGroup.class,UpdateGroup.class})privateStringname;}// 3. Controller 中使用 Validated 指定分组RestControllerpublicclassUserController{PostMapping(/user)publicStringcreate(Validated(CreateGroup.class)RequestBodyUserDtodto){// 只校验 CreateGroup 分组下的约束id 必须为空returncreated;}PutMapping(/user)publicStringupdate(Validated(UpdateGroup.class)RequestBodyUserDtodto){// 只校验 UpdateGroup 分组下的约束id 必须不为空returnupdated;}}3.校验异常及相关处理3.1三种校验异常及触发场景异常类触发场景MethodArgumentNotValidExceptionRequestBody Validated 参数校验失败ConstraintViolationException方法级参数直接校验失败非 BodyBindException表单/Query 参数绑定校验失败//1.MethodArgumentNotValidExceptionPostMapping()publicRVoidadd(Validated(AddGroup.class)RequestBodySecIllegalDrivingBobo){returntoAjax(secIllegalDrivingService.insertByBo(bo));}//2.ConstraintViolationExceptionGetMapping(/{id})publicRSecIllegalDrivingVogetInfo(NotNull(message主键不能为空)PathVariableLongid){returnR.ok(secIllegalDrivingService.queryById(id));}//3.BindExceptionGetMapping(/list)publicTableDataInfoSecIllegalDrivingVolist(SecIllegalDrivingBobo,PageQuerypageQuery){returnsecIllegalDrivingService.queryPageList(bo,pageQuery);}3.2异常处理每种异常的处理都遵循 “记录日志 提取 message 统一返回” 三步模式。MethodArgumentNotValidExceptionBody 校验ExceptionHandler(MethodArgumentNotValidException.class)publicRVoidhandleMethodArgumentNotValidException(MethodArgumentNotValidExceptione){//1.写入日志log.error(e.getMessage());//2.提取所有字段的 message用 , 拼接StringmessageStreamUtils.join(e.getBindingResult().getAllErrors(),DefaultMessageSourceResolvable::getDefaultMessage,, );//3.统一响应返回前端returnR.fail(message);}ConstraintViolationException方法参数校验ExceptionHandler(ConstraintViolationException.class)publicRVoidconstraintViolationException(ConstraintViolationExceptione){//1.写入日志log.error(e.getMessage());//2.从 ConstraintViolation 集合中提取 getMessage()用 , 拼接StringmessageStreamUtils.join(e.getConstraintViolations(),ConstraintViolation::getMessage,, );//3.统一响应返回前端returnR.fail(message);}BindException表单绑定校验ExceptionHandler(BindException.class)publicRVoidhandleBindException(BindExceptione){//1.写入日志log.error(e.getMessage());//2.获取并拼接异常信息StringmessageStreamUtils.join(e.getAllErrors(),DefaultMessageSourceResolvable::getDefaultMessage,, );//3.统一响应返回前端returnR.fail(message);}