千家信息网

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

发表于:2025-12-01 作者:千家信息网编辑
千家信息网最后更新 2025年12月01日,今天就跟大家聊聊有关Spring Boot 中怎么利用JSR303 实现参数验证,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。Bean Val
千家信息网最后更新 2025年12月01日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安全错误 数据库的锁怎样保障安全 gprs模块代理服务器 java手写http服务器 网络安全与执法就业有优势吗 小学二年级网络安全教育活动方案 迈克菲网络安全卸载 水务行业网络安全费用 数据库导出到excel 钮门网络技术有限公司 提取数据库中的json数据 数据库的悲观锁 原理 悟空软件开发好用吗 海南神灯网络技术有限公司 数据库最常见的数据类型 计算机网络技术面试自我介绍中专 江苏小型软件开发技术指导 软件定义网络技术是什么 服务器磁盘空间满了有什么影响 网络安全职业规划书 死亡之夜专用服务器架设 惠州小程序软件开发 最好的网络安全国家 数据库实时计算响应慢 信息管理中的数据库作用 java实训访问数据库 基岩版服务器管理权限 华夏免费版数据库修改教程 软件开发功能需求确认 内蒙古和君网络技术有限公司 服务器不支持外接无线吗 网络安全部署第二版课后答案
0