千家信息网

Spring Boot 中怎么利用JSR303 实现参数验证

发表于:2026-10-31 作者:千家信息网编辑
千家信息网最后更新 2026年10月31日,今天就跟大家聊聊有关Spring Boot 中怎么利用JSR303 实现参数验证,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。Bean Val
千家信息网最后更新 2026年10月31日Spring Boot 中怎么利用JSR303 实现参数验证

今天就跟大家聊聊有关Spring Boot 中怎么利用JSR303 实现参数验证,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。


Bean Validation 规范内嵌的约束注解

实例

基本应用

引入依赖

    org.springframework.boot    spring-boot-starter-validation

给参数对象添加校验注解

@Datapublic class User {    private Integer id;    @NotBlank(message = "用户名不能为空")    private String username;    @Pattern(regexp = "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{8,16}$", message = "密码必须为8~16个字母和数字组合")    private String password;    @Email    private String email;    private Integer gender;}

Controller 中需要校验的参数Bean前添加 @Valid 开启校验功能,紧跟在校验的Bean后添加一个BindingResult,BindingResult封装了前面Bean的校验结果。

@RestController@RequestMapping("/user")public class UserController {    @PostMapping("")    public Result save (@Valid User user , BindingResult bindingResult)  {        if (bindingResult.hasErrors()) {            Map map = new HashMap<>();            bindingResult.getFieldErrors().forEach( (item) -> {                String message = item.getDefaultMessage();                String field = item.getField();                map.put( field , message );            } );            return Result.build( 400 , "非法参数 !" , map);        }        return Result.ok();    }}

测试如下:

异常的统一处理

参数校验不通过时,会抛出 BingBindException 异常,可以在统一异常处理中,做统一处理,这样就不用在每个需要参数校验的地方都用 BindingResult 获取校验结果了。

@Slf4j@RestControllerAdvice(basePackages = "com.itwolfed.controller")public class GlobalExceptionControllerAdvice {    @ExceptionHandler(value= {MethodArgumentNotValidException.class , BindException.class})    public Result handleVaildException(Exception e){        BindingResult bindingResult = null;        if (e instanceof MethodArgumentNotValidException) {            bindingResult = ((MethodArgumentNotValidException)e).getBindingResult();        } else if (e instanceof BindException) {            bindingResult = ((BindException)e).getBindingResult();        }        Map errorMap = new HashMap<>(16);        bindingResult.getFieldErrors().forEach((fieldError)->                errorMap.put(fieldError.getField(),fieldError.getDefaultMessage())        );        return Result.build(400 , "非法参数 !" , errorMap);    }}

分组解决校验

新增和修改对于实体的校验规则是不同的,例如id是自增的时,新增时id要为空,修改则必须不为空;新增和修改,若用的恰好又是同一种实体,那就需要用到分组校验。

校验注解都有一个groups属性,可以将校验注解分组,我们看下 @NotNull的源码:

@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })@Retention(RUNTIME)@Repeatable(List.class)@Documented@Constraint(validatedBy = { })public @interface NotNull {    String message() default "{javax.validation.constraints.NotNull.message}";    Class[] groups() default { };    Class[] payload() default { };    @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })    @Retention(RUNTIME)    @Documented    @interface List {        NotNull[] value();    }}

从源码可以看出 groups 是一个Class类型的数组,那么就可以创建一个Groups.

public class Groups {    public interface Add{}    public interface  Update{}}

给参数对象的校验注解添加分组

@Datapublic class User {    @Null(message = "新增不需要指定id" , groups = Groups.Add.class)    @NotNull(message = "修改需要指定id" , groups = Groups.Update.class)    private Integer id;    @NotBlank(message = "用户名不能为空")    @NotNull    private String username;    @Pattern(regexp = "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{8,16}$", message = "密码必须为8~16个字母和数字组合")    private String password;    @Email    private String email;    private Integer gender;}

Controller 中原先的 @Valid不能指定分组 ,需要替换成 @Validated

@RestController@RequestMapping("/user")public class UserController {    @PostMapping("")    public Result save (@Validated(Groups.Add.class) User user)  {        return Result.ok();    }}

测试如下:

自定义校验注解

虽然JSR303和springboot-validator 已经提供了很多校验注解,但是当面对复杂参数校验时,还是不能满足我们的要求,这时候我们就需要 自定义校验注解。

例如User中的gender,用 1代表男 2代表女,我们自定义一个校验注解 @ListValue,指定取值只能1和2。

创建约束规则
@Documented@Constraint(validatedBy = { ListValueConstraintValidator.class })@Target({ METHOD, FIELD, ANNOTATION_TYPE })@Retention(RUNTIME)public @interface ListValue {    String message() default "";    Class[] groups() default { };    Class[] payload() default { };    int[] vals() default { };}

一个标注(annotation) 是通过 @interface关键字来定义的. 这个标注中的属性是声明成类似方法
的样式的. 根据Bean Validation API 规范的要求:

  • message属性, 这个属性被用来定义默认得消息模版, 当这个约束条件被验证失败的时候,通过
    此属性来输出错误信息。

  • groups 属性, 用于指定这个约束条件属于哪(些)个校验组. 这个的默认值必须是Class类型数组。

  • payload 属性, Bean Validation API 的使用者可以通过此属性来给约束条件指定严重级别. 这个属性并不被API自身所使用。

除了这三个强制性要求的属性(message, groups 和 payload) 之外, 我们还添
加了一个属性用来指定所要求的值. 此属性的名称vals在annotation的定义中比较特
殊, 如果只有这个属性被赋值了的话, 那么, 在使用此annotation到时候可以忽略此属性名称.

另外, 我们还给这个annotation标注了一些元标注( meta
annotatioins):

  • @Target({ METHOD, FIELD, ANNOTATION_TYPE }): 表示此注解可以被用在方法, 字段或者
    annotation声明上。

  • @Retention(RUNTIME): 表示这个标注信息是在运行期通过反射被读取的.

  • @Constraint(validatedBy = ListValueConstraintValidator.class): 指明使用哪个校验器(类) 去校验使用了此标注的元素.

  • @Documented: 表示在对使用了该注解的类进行javadoc操作到时候, 这个标注会被添加到
    javadoc当中.

创建约束校验器
import javax.validation.ConstraintValidator;import javax.validation.ConstraintValidatorContext;import java.util.HashSet;import java.util.Set;public class ListValueConstraintValidator implements ConstraintValidator {    private Set set = new HashSet<>();    /**     * 初始化方法     */    @Override    public void initialize(ListValue constraintAnnotation) {        int[] vals = constraintAnnotation.vals();        for (int val : vals) {            set.add(val);        }    }    /**     * 判断是否校验成功     *     * @param value 需要校验的值     * @param context     * @return     */    @Override    public boolean isValid(Integer value, ConstraintValidatorContext context) {        return set.contains(value);    }}

ListValueConstraintValidator定义了两个泛型参数, 第一个是这个校验器所服务到标注类型(在我们的例子中即ListValue), 第二个这个校验器所支持到被校验元素的类型 (即Integer)。

如果一个约束标注支持多种类型的被校验元素的话, 那么需要为每个所支持的类型定义一个ConstraintValidator,并且注册到约束标注中。

这个验证器的实现就很平常了, initialize() 方法传进来一个所要验证的标注类型的实例, 在本
例中, 我们通过此实例来获取其vals属性的值,并将其保存为Set集合中供下一步使
用。

isValid()是实现真正的校验逻辑的地方, 判断一个给定的int对于 @ListValue这个约束条件来说
是否是合法的。

在参数对象中使用 @ListValue注解。

@Datapublic class User {    @Null(message = "新增不需要指定id" , groups = Groups.Add.class)    @NotNull(message = "修改需要指定id" , groups = Groups.Update.class)    private Integer id;    @NotBlank(message = "用户名不能为空")    @NotNull    private String username;    @Pattern(regexp = "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{8,16}$", message = "密码必须为8~16个字母和数字组合")    private String password;    @Email    private String email;    @ListValue( message = "性别应指定相应的值" , vals = {1,2} , groups = {Groups.Add.class , Groups.Update.class})    private Integer gender;}

测试如下:

看完上述内容,你们对Spring Boot 中怎么利用JSR303 实现参数验证有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注行业资讯频道,感谢大家的支持。

属性 参数 注解 类型 验证 分组 方法 条件 校验器 支持 元素 内容 字母 实例 密码 对象 数字 用户 用户名 处理 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 中关村互联网 科技集团 互联网科技与金融科技 互联网 科技公司 唐信互联网科技 兼蜜互联网科技 上游互联网科技 互联网汽车科技小镇 顶级互联网科技成果有哪些 重庆互联网医院沙龙医盟信息科技 想开互联网科技公司怎么样 中国科技创新与世界互联网大会 南博望互联网科技有限公司 高科技互联网套路 浙南科技城互联网大厦招聘 互联网高科技行业有哪些企业 保山互联网科技靠谱吗 黑龙江互联网科技公司 安徽九吞吴互联网科技有限公司 青岛互联网科技园 恒生互联网科技趋势 亿人帮互联网科技有限公司 西安纸贵互联网科技有限公司 伴你行互联网科技有限公司 互联网还是高科技吗 赶超互联网科技 微信多开 互联网金融科技名词解释 区块链结合互联网科技 深圳互联网科技的发展 上海万顺科技互联网有限公司招聘 重庆科技产业互联网有哪些
0