SpringBoot Bean Validation 参数校验——从入门到自定义注解

📅 2026/7/14 23:30:43
SpringBoot Bean Validation 参数校验——从入门到自定义注解
参数校验是后端接口的第一道防线。如果让前端传过来的脏数据直接进入业务逻辑后面排查起来非常麻烦。Bean ValidationJSR 303提供了一套声明式校验注解让校验逻辑和业务逻辑分离。一、为什么需要参数校验// ❌ 不校验参数直接用PostMapping(/user)publicResultVOUsercreateUser(RequestBodyUseruser){// 如果 user.name 为空保存到数据库后会报错// 如果 user.age -1业务逻辑会出问题// 如果 user.email 格式不对其他地方会解析失败returnResultVO.success(userService.save(user));}// ✅ 使用注解校验PostMapping(/user)publicResultVOUsercreateUser(RequestBodyValidUseruser){returnResultVO.success(userService.save(user));}二、常用校验注解DatapublicclassUserCreateDTO{NotBlank(message用户名不能为空)Size(min2,max20,message用户名长度必须在{min}-{max}之间)privateStringusername;NotBlank(message密码不能为空)Pattern(regexp^(?.*[a-zA-Z])(?.*\\d).{6,20}$,message密码必须包含字母和数字长度6-20)privateStringpassword;NotBlank(message手机号不能为空)Pattern(regexp^1[3-9]\\d{9}$,message手机号格式不正确)privateStringphone;Email(message邮箱格式不正确)privateStringemail;NotNull(message年龄不能为空)Min(value0,message年龄不能小于0)Max(value150,message年龄不能大于150)privateIntegerage;NotNull(message性别不能为空)Range(min0,max2,message性别值不正确)privateIntegergender;DecimalMin(value0.01,message金额不能小于0.01)DecimalMax(value999999.99,message金额超限)privateBigDecimalamount;Future(message过期时间必须是将来的时间)privateLocalDateTimeexpireTime;Size(min1,max10,message角色数量1-10个)privateListNotBlankStringroles;}三、分组校验// 定义分组接口publicinterfaceCreateGroup{}publicinterfaceUpdateGroup{}DatapublicclassUserDTO{Null(groupsCreateGroup.class,message新增时ID必须为空)NotNull(groupsUpdateGroup.class,message修改时ID不能为空)privateLongid;NotBlank(groupsCreateGroup.class,message用户名不能为空)privateStringusername;NotBlank(groupsCreateGroup.class,message密码不能为空)privateStringpassword;privateStringemail;}PostMappingpublicResultVO?create(RequestBodyValidated(CreateGroup.class)UserDTOdto){returnResultVO.success(userService.create(dto));}PutMapping(/{id})publicResultVO?update(PathVariableLongid,RequestBodyValidated(UpdateGroup.class)UserDTOdto){dto.setId(id);returnResultVO.success(userService.update(dto));}四、自定义校验注解1. 创建注解Target({ElementType.FIELD,ElementType.PARAMETER})Retention(RetentionPolicy.RUNTIME)Constraint(validatedByPhoneValidator.class)publicinterfacePhone{Stringmessage()default手机号格式不正确;Class?[]groups()default{};Class?extendsPayload[]payload()default{};}2. 实现校验器publicclassPhoneValidatorimplementsConstraintValidatorPhone,String{privatestaticfinalPatternPHONE_PATTERNPattern.compile(^1[3-9]\\d{9}$);OverridepublicbooleanisValid(Stringvalue,ConstraintValidatorContextcontext){if(valuenull||value.isEmpty()){returntrue;// 空值由 NotBlank 处理}returnPHONE_PATTERN.matcher(value).matches();}}3. 使用DatapublicclassUserDTO{Phone(message手机号格式错误)privateStringphone;}五、校验异常处理RestControllerAdvicepublicclassValidationExceptionHandler{ExceptionHandler(MethodArgumentNotValidException.class)publicResultVO?handleValidation(MethodArgumentNotValidExceptione){Stringmsge.getBindingResult().getFieldErrors().stream().map(err-err.getField(): err.getDefaultMessage()).collect(Collectors.joining(; ));returnResultVO.error(400,msg);}ExceptionHandler(ConstraintViolationException.class)publicResultVO?handleConstraint(ConstraintViolationExceptione){Stringmsge.getConstraintViolations().stream().map(v-v.getPropertyPath(): v.getMessage()).collect(Collectors.joining(; ));returnResultVO.error(400,msg);}}六、常用注解速查注解作用支持类型NotNull值不能为 null所有类型NotBlank字符串不能为 null 且不能全空格StringNotEmpty集合/字符串不能为空String/Collection/Map/数组Size长度/大小在指定范围内String/Collection/Map/数组Min/Max数值最小值/最大值数值类型Range数值在指定范围内数值类型Email邮箱格式StringPattern正则表达式匹配StringPast/Future过去/将来的时间Date/LocalDateDecimalMin/DecimalMax精确数值范围BigDecimal 觉得有用的话点赞 关注【张老师技术栈】吧