JSR303校验 + 统一异常处理方法

JSR303校验 + 统一异常处理方法,第1张

JSR303校验 + 统一异常处理方法 3.JSR303校验 + 统一异常处理方法

问题引入:填写form时应该有前端校验,后端也应该有校验

对于后端来说,外界传递的一切参数都不可靠。

前端的校验是element-ui表单验证https://element.eleme.cn/#/zh-CN/component/form

Form 组件提供了表单验证的功能,只需要通过 rules 属性传入约定的验证规则,并将 Form-Item 的 prop 属性设置为需校验的字段名即可。校验规则参见 async-validator

使用自定义校验规则可以解决字母限制的问题

var validatePass2 = (rule, value, callback) => {
    if (value === '') {
        callback(new Error('请再次输入密码'));
    } else if (value !== this.ruleForm.pass) {
        callback(new Error('两次输入密码不一致!'));
    } else {
        callback();
    }
};
return {
    rules: {
        checkPass: [
            { validator: validatePass2, trigger: 'blur' }
        ],

后端:@NotNull等

1.@NotNull等

步骤1:使用校验注解

在Java中提供了一系列的校验方式,它这些校验方式在“javax.validation.constraints”包中,提供了如@Email,@NotNull等注解。


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

里面依赖了hibernate-validator

在非空处理方式上提供了@NotNull,@NotBlank和@NotEmpty

(1)@NotNull 该属性不能为null

(2)@NotEmpty 该字段不能为null或""

支持以下几种类型

1.CharSequence (length of character sequence is evaluated)字符序列(字符序列长度的计算)
2.Collection (collection size is evaluated) 集合长度的计算
3.Map (map size is evaluated) map长度的计算
4.Array (array length is evaluated) 数组长度的计算
5.上面什么意思呢?就是说如果标注的是map,它会帮你看长度

(3)@NotBlank:不能为空,不能仅为一个空格

2.@Valid内置异常

这里内置异常的意思是发生异常时返回的json不是我们的R对象,而是mvc的内置类

步骤2:controller中加校验注解@Valid,开启校验,

@RequestMapping("/save")
public R save(@Valid @RequestBody BrandEntity brand){
    brandService.save(brand);
return R.ok();
}

测试: http://localhost:88/api/product/brand/save

在postman种发送上面的请求,可以看到返回的甚至不是R对象

{
    "timestamp": "2020-04-29T09:20:46.383+0000",
    "status": 400,
    "error": "Bad Request",
    "errors": [
        {
            "codes": [
                "NotBlank.brandEntity.name",
                "NotBlank.name",
                "NotBlank.java.lang.String",
                "NotBlank"
            ],
            "arguments": [
                {
                    "codes": [
                        "brandEntity.name",
                        "name"
                    ],
                    "arguments": null,
                    "defaultMessage": "name",
                    "code": "name"
                }
            ],
            "defaultMessage": "不能为空",
            "objectName": "brandEntity",
            "field": "name",
            "rejectedValue": "",
            "bindingFailure": false,
            "code": "NotBlank"
        }
    ],
    "message": "Validation failed for object='brandEntity'. Error count: 1",
    "path": "/product/brand/save"
}

能够看到"defaultMessage": “不能为空”,这些错误消息定义在“hibernate-validator”的“orghibernatevalidatorValidationMessages_zh_CN.properties”文件中。在该文件中定义了很多的错误规则:

javax.validation.constraints.AssertFalse.message     = 只能为false
javax.validation.constraints.AssertTrue.message      = 只能为true
javax.validation.constraints.DecimalMax.message      = 必须小于或等于{value}
javax.validation.constraints.DecimalMin.message      = 必须大于或等于{value}
javax.validation.constraints.Digits.message          = 数字的值超出了允许范围(只允许在{integer}位整数和{fraction}位小数范围内)
javax.validation.constraints.Email.message           = 不是一个合法的电子邮件地址
javax.validation.constraints.Future.message          = 需要是一个将来的时间
javax.validation.constraints.FutureOrPresent.message = 需要是一个将来或现在的时间
javax.validation.constraints.Max.message             = 最大不能超过{value}
javax.validation.constraints.Min.message             = 最小不能小于{value}
javax.validation.constraints.Negative.message        = 必须是负数
javax.validation.constraints.NegativeOrZero.message  = 必须是负数或零
javax.validation.constraints.NotBlank.message        = 不能为空
javax.validation.constraints.NotEmpty.message        = 不能为空
javax.validation.constraints.NotNull.message         = 不能为null
javax.validation.constraints.Null.message            = 必须为null
javax.validation.constraints.Past.message            = 需要是一个过去的时间
javax.validation.constraints.PastOrPresent.message   = 需要是一个过去或现在的时间
javax.validation.constraints.Pattern.message         = 需要匹配正则表达式"{regexp}"
javax.validation.constraints.Positive.message        = 必须是正数
javax.validation.constraints.PositiveOrZero.message  = 必须是正数或零
javax.validation.constraints.Size.message            = 个数必须在{min}和{max}之间

org.hibernate.validator.constraints.CreditCardNumber.message        = 不合法的xyk号码
org.hibernate.validator.constraints.Currency.message                = 不合法的货币 (必须是{value}其中之一)
org.hibernate.validator.constraints.EAN.message                     = 不合法的{type}条形码
org.hibernate.validator.constraints.Email.message                   = 不是一个合法的电子邮件地址
org.hibernate.validator.constraints.Length.message                  = 长度需要在{min}和{max}之间
org.hibernate.validator.constraints.CodePointLength.message         = 长度需要在{min}和{max}之间
org.hibernate.validator.constraints.LuhnCheck.message               = ${validatedValue}的校验码不合法, Luhn模10校验和不匹配
org.hibernate.validator.constraints.Mod10Check.message              = ${validatedValue}的校验码不合法, 模10校验和不匹配
org.hibernate.validator.constraints.Mod11Check.message              = ${validatedValue}的校验码不合法, 模11校验和不匹配
org.hibernate.validator.constraints.ModCheck.message                = ${validatedValue}的校验码不合法, ${modType}校验和不匹配
org.hibernate.validator.constraints.NotBlank.message                = 不能为空
org.hibernate.validator.constraints.NotEmpty.message                = 不能为空
org.hibernate.validator.constraints.ParametersscriptAssert.message  = 执行脚本表达式""没有返回期望结果
org.hibernate.validator.constraints.Range.message                   = 需要在{min}和{max}之间
org.hibernate.validator.constraints.SafeHtml.message                = 可能有不安全的HTML内容
org.hibernate.validator.constraints.scriptAssert.message            = 执行脚本表达式""没有返回期望结果
org.hibernate.validator.constraints.URL.message                     = 需要是一个合法的URL

org.hibernate.validator.constraints.time.DurationMax.message        = 必须小于${inclusive == true ? '或等于' : ''}${days == 0 ? '' : days += '天'}${hours == 0 ? '' : hours += '小时'}${minutes == 0 ? '' : minutes += '分钟'}${seconds == 0 ? '' : seconds += '秒'}${millis == 0 ? '' : millis += '毫秒'}${nanos == 0 ? '' : nanos += '纳秒'}
org.hibernate.validator.constraints.time.DurationMin.message        = 必须大于${inclusive == true ? '或等于' : ''}${days == 0 ? '' : days += '天'}${hours == 0 ? '' : hours += '小时'}${minutes == 0 ? '' : minutes += '分钟'}${seconds == 0 ? '' : seconds += '秒'}${millis == 0 ? '' : millis += '毫秒'}${nanos == 0 ? '' : nanos += '纳秒'}

想要自定义错误消息,可以覆盖默认的错误提示信息,如@NotBlank的默认message是

public @interface NotBlank {
String message() default "{javax.validation.constraints.NotBlank.message}";

可以在添加注解的时候,修改message:

@NotBlank(message = "品牌名必须非空")
private String name;

当再次发送请求时,得到的错误提示信息:

{
    "timestamp": "2020-04-29T09:36:04.125+0000",
    "status": 400,
    "error": "Bad Request",
    "errors": [
        {
            "codes": [
                "NotBlank.brandEntity.name",
                "NotBlank.name",
                "NotBlank.java.lang.String",
                "NotBlank"
            ],
            "arguments": [
                {
                    "codes": [
                        "brandEntity.name",
                        "name"
                    ],
                    "arguments": null,
                    "defaultMessage": "name",
                    "code": "name"
                }
            ],
            "defaultMessage": "品牌名必须非空",
            "objectName": "brandEntity",
            "field": "name",
            "rejectedValue": "",
            "bindingFailure": false,
            "code": "NotBlank"
        }
    ],
    "message": "Validation failed for object='brandEntity'. Error count: 1",
    "path": "/product/brand/save"
}

但是返回的错误不是R对象,影响接收端的接收,我们可以通过局部异常处理或者统一一次处理解决

3.局部异常处理BindResult

步骤3:给校验的Bean后,紧跟一个BindResult,就可以获取到校验的结果。拿到校验的结果,就可以自定义的封装。

如下两个方法是一体的

@RequestMapping("/save")
public R save(@Valid @RequestBody BrandEntity brand){
    brandService.save(brand);
return R.ok();
}

@RequestMapping("/save")
public R save(@Valid @RequestBody BrandEntity brand,
              BindingResult result){ // 手动处理异常
              
              if( result.hasErrors()){
    Map map=new HashMap<>();
    //1.获取错误的校验结果
    result.getFieldErrors().forEach((item)->{
        //获取发生错误时的message
        String message = item.getDefaultMessage();
        //获取发生错误的字段
        String field = item.getField();
        map.put(field,message);
    });
    return R.error(400,"提交的数据不合法").put("data",map);
}else {

}
brandService.save(brand);

return R.ok();

这种是针对于该请求设置了一个内容校验,如果针对于每个请求都单独进行配置,显然不是太合适,实际上可以统一的对于异常进行处理。

4.统一异常处理@ExceptionHandler

上文说到 @ ExceptionHandler 需要进行异常处理的方法必须与出错的方法在同一个Controller里面。那么当代码加入了 @ControllerAdvice,则不需要必须在同一个 controller 中了。这也是 Spring 3.2 带来的新特性。从名字上可以看出大体意思是控制器增强。 也就是说,@controlleradvice + @ ExceptionHandler 也可以实现全局的异常捕捉。

(1)抽取一个异常处理类

@ControllerAdvice标注在类上,通过“basePackages”能够说明处理哪些路径下的异常。
@ExceptionHandler(value = 异常类型.class)标注在方法上

@Slf4j
@RestControllerAdvice(basePackages = "com.atguigu.gulimall.product.controller")//管理的controller
public class GulimallExceptionControllerAdvice {
    
@ExceptionHandler(value = Exception.class) // 也可以返回ModelAndView
public R handlevalidException(MethodArgumentNotValidException exception){

    Map map=new HashMap<>();
    // 获取数据校验的错误结果
    BindingResult bindingResult = exception.getBindingResult();
    // 处理错误
    bindingResult.getFieldErrors().forEach(fieldError -> {
        String message = fieldError.getDefaultMessage();
        String field = fieldError.getField();
        map.put(field,message);
    });

    log.error("数据校验出现问题{},异常类型{}",exception.getMessage(),exception.getClass());

    return R.error(400,"数据校验出现问题").put("data",map);
}
}

(2)测试: http://localhost:88/api/product/brand/save

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZirxsX9l-1642404848092)(D:%5Ccxy%5Cjava%E6%97%A5%E7%A7%AF%E6%9C%88%E7%B4%AF%5C%E6%97%A5%E7%A7%AF%E6%9C%88%E7%B4%AF.assets%5Cimage-20200429183334783.png)]

(3)默认异常处理

@ExceptionHandler(value = Throwable.class)//异常的范围更大
public R handleException(Throwable throwable){
    log.error("未知异常{},异常类型{}",
              throwable.getMessage(),
              throwable.getClass());
    return R.error(BizCodeEnum.UNKNOW_EXEPTION.getCode(),
                   BizCodeEnum.UNKNOW_EXEPTION.getMsg());
}

(4)错误状态码
上面代码中,针对于错误状态码,是我们进行随意定义的,然而正规开发过程中,错误状态码有着严格的定义规则,如该在项目中我们的错误状态码定义

上面的用法主要是通过@Controller+@ExceptionHandler来进行异常拦截处理

BizCodeEnum

为了定义这些错误状态码,我们可以单独定义一个常量类(一般定义为枚举类),用来存储这些错误状态码

package com.atguigu.common.exception;


  public enum BizCodeEnum {

  UNKNOW_EXEPTION(10000,"系统未知异常"),

  VALID_EXCEPTION( 10001,"参数格式校验失败");

  private int code;
  private String msg;

  BizCodeEnum(int code, String msg) {
      this.code = code;
      this.msg = msg;
  }

  public int getCode() {
      return code;
  }

  public String getMsg() {
      return msg;
  }
  }

(5)测试: http://localhost:88/api/product/brand/save

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fJtELzat-1642404848095)(D:%5Ccxy%5Cjava%E6%97%A5%E7%A7%AF%E6%9C%88%E7%B4%AF%5C%E6%97%A5%E7%A7%AF%E6%9C%88%E7%B4%AF.assets%5Cimage-20200429191830967.png)]

可以参考下:https://blog.csdn.net/github_36086968/article/details/103115128

补充详解:

统一异常处理:

SpringBoot中有一个@ControllerAdvice的注解,使用该注解即表示开启全局异常捕获,接下来我们只需在自定义的方法上使用@ExceptionHandler注解,并定义捕获异常的类型,那么这个自定义的方法体就是对这种类型的异常进行统一的处理。

同时还提供@RestControllerAdvice注解,就相当于@RestController。

@RestController = @Controller + @ResponseBody@RestControllerAdvice = @ControllerAdvice + @ResponseBody

统一异常处理会优先处理子类异常,也就是说当出现了空指针异常,那么会优先交给handleNullPointerException这个方法处理,如果没有明确的针对某种异常进行处理,那么handleException这个方法会自觉的处理它们。

后端无论是怎么报错,返回给前端的永远是JSON数据,之后前端就可以该跳转错误页面的跳转,该d框警告的d框!

举例:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result implements Serializable {
    private static final long serialVersionUID = 1L;

    //业务码,比如成功、失败、权限不足等 code,可自行定义
    @ApiModelProperty("返回码")
    private int status;

    //返回信息,后端在进行业务处理后返回给前端一个提示信息,可自行定义
    @ApiModelProperty("返回信息")
    private String message;

    //数据结果,泛型,可以是列表、单个对象、数字、布尔值等
    @ApiModelProperty("返回数据")
    private T data;


    public Result(int resultCode, String message) {
        this.status = resultCode;
        this.message = message;
    }
    
    


public class ResultGenerator {
    private static final String DEFAULT_SUCCESS_MESSAGE = "SUCCESS";
    private static final String DEFAULT_FAIL_MESSAGE = "FAIL";
    private static final int RESULT_CODE_SUCCESS = 200;
    private static final int RESULT_CODE_SERVER_ERROR = 500;

    //默认成功返回success
    public static Result genSuccessResult() {
        Result result = new Result();
        result.setStatus(RESULT_CODE_SUCCESS);
        result.setMessage(DEFAULT_SUCCESS_MESSAGE);
        return result;
    }

    //自定义成功时的message
    public static Result genSuccessResult(String message) {
        Result result = new Result();
        result.setStatus(RESULT_CODE_SUCCESS);
        result.setMessage(message);
        return result;
    }

    //默认成功返回success并且传入返回结果
    public static Result genSuccessResult(Object data) {
        Result result = new Result();
        result.setStatus(RESULT_CODE_SUCCESS);
        result.setMessage(DEFAULT_SUCCESS_MESSAGE);
        result.setData(data);
        return result;
    }

    //默认失败500状态码,不传入错误消息时默认fail,否则为传入自定义错误消息
    public static Result genFailResult(String message) {
        Result result = new Result();
        result.setStatus(RESULT_CODE_SERVER_ERROR);
        if (StringUtils.isEmpty(message)) {
            result.setMessage(DEFAULT_FAIL_MESSAGE);
        } else {
            result.setMessage(message);
        }
        return result;
    }

    //自定义错误状态码和消息
    public static Result genErrorResult(int code, String message) {
        Result result = new Result();
        result.setStatus(code);
        result.setMessage(message);
        return result;
    }
}

@RestControllerAdvice
public class MallExceptionHandler {

    //一般参数校验绑定异常处理
    @ExceptionHandler(BindException.class)
    public Object bindException1(BindException e) {
        Result result = new Result();
        result.setStatus(510);
        List fieldErrors = e.getBindingResult().getFieldErrors();
        //多个错误,取第一个
        FieldError error = fieldErrors.get(0);
        String msg = error.getDefaultMessage();
        result.setMessage(msg);
        return result;
    }

    //JSON参数校验绑定异常处理
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Object bindException2(MethodArgumentNotValidException e) {
        Result result = new Result();
        result.setStatus(510);
        List fieldErrors = e.getBindingResult().getFieldErrors();
        //多个错误,取第一个
        FieldError error = fieldErrors.get(0);
        String msg = error.getDefaultMessage();
        result.setMessage(msg);
        return result;
    }


    @ExceptionHandler(Exception.class)
    public Object handleException(Exception e, HttpServletRequest req) {
        Result result = new Result();
        result.setStatus(500);
        //区分是否为自定义异常
        if (e instanceof MallException) {
            result.setMessage(e.getMessage());
            //无效认证!请重新登录!||未登录!
            if (e.getMessage().equals("未登录!") || e.getMessage().equals("无效认证!请重新登录!")) {
                result.setStatus(416);
            }else if (e.getMessage().equals("管理员未登录!") || e.getMessage().equals("管理员登录过期!请重新登录!")) {
                result.setStatus(419);
            }
        } else if (e instanceof DuplicateKeyException){
            result.setMessage("重复!");
        }else {
            e.printStackTrace();
            result.setMessage("未知异常,请联系管理员");
        }
        return result;

    }
}

5.分组校验功能(多场景校验)

前面解决了统一异常处理,但是现状有新的需求是对同一实体类参数也要区分场景

如果新增和修改两个接口需要验证的字段不同,比如id字段,新增可以不传递,但是修改必须传递id,我们又不可能写两个vo来满足不同的校验规则。所以就需要用到分组校验来实现。

步骤:

1.创建分组接口Insert.class Update.class
2.在VO的属性中标注@NotBlank等注解,并指定要使用的分组,如@NotNull(message = “用户姓名不能为空”,groups = {Insert.class,Update.class})
3.controller的方法上或者方法参数上写要处理的分组的接口信息,如@Validated(AddGroup.class)

1、@NotNull(groups={A.class})
1、给校验注解,标注上groups,指定什么情况下才需要进行校验

如:指定在更新和添加的时候,都需要进行校验。新增时不需要带id,修改时必须带id

在实体类的统一属性上添加多个不同的校验注解

@NotNull(message = "修改必须定制品牌id", groups = {UpdateGroup.class})
@Null(message = "新增不能指定id", groups = {AddGroup.class})
@TableId
private Long brandId;



@NotBlank(groups = {AddGroup.class})
@URL(message = "logo必须是一个合法的URL地址", groups={AddGroup.class, UpdateGroup.class})
private String logo;
注意上面因为@NotBlank没有指定UpdateGroup分组,所以不生效。此时update时可以不携带,但带了一定得是url地址

在这种情况下,没有指定分组的校验注解,默认是不起作用的。想要起作用就必须要加groups。

2、@Validated
业务方法参数上使用@Validated注解

@Validated的value值指定要使用的一个或多个分组

JSR-303 defines validation groups as custom annotations which an application declares for the sole purpose of using
them as type-safe group arguments, as implemented in SpringValidatorAdapter.

JSR-303 将验证组定义为自定义注释,应用程序声明的唯一目的是将它们用作类型安全组参数,如 SpringValidatorAdapter 中实现的那样。

Other SmartValidator implementations may support class arguments in other ways as well.

其他SmartValidator 实现也可以以其他方式支持类参数。

// 新增场景添加 新增分组注解
@RequestMapping("/save")  
public R save(@Validated(AddGroup.class) @RequestBody BrandEntity brand) {
    brandService.save(brand);
return R.ok();
}

// 删除场景添加 删除分组注解
@RequestMapping("/delete")
public R delete(@RequestBody Long[] brandIds) {
    brandService.removeByIds(Arrays.asList(brandIds));
    return R.ok();
}

总结:controller接收到之后,根据@Validated表明的分组信息,品牌对应的校验注解。

3、分组校验的默认校验
这里要是指定了分组,实体类上的注解就是指定了分组的注解才生效,

没有指定分组的默认不生效,要是没有指定分组,就是对没有指定分组的注解生效,指定分组的注解就不生效了

可以在自定义的异常分组接口中继承Default类。所有没有写明group的都属于Default分组。

此外还可以在实体类上标注@GroupSequece({A.class,B.class})指定校验顺序

通过@GroupSequence指定验证顺序:先验证A分组,如果有错误立即返回而不会验证B分组,接着如果A分组验证通过了,那么才去验证B分组,最后指定User.class表示那些没有分组的在最后。这样我们就可以实现按顺序验证分组了。

关于Default,此处我springvalidation默认生成的验证接口,验证的范围是所有带有验证信息的属性,

若是属性上方写了验证组,则是验证该组内的属性

若是验证实体类类上写了GroupSequence({}) 则说明重写了Default验证接口,Default就按照GroupSequence里所写的组信息进行验证
6.自定义校验注解

Hibernate Validator提供了一系列内置的校验注解,可以满足大部分的校验需求。但是,仍然有一部分校验需要特殊定制,例如某个字段的校验,我们提供两种校验强度,当为normal强度时我们除了<>号之外,都允许出现。当为strong强度时,我们只允许出现常用汉字,数字,字母。内置的注解对此则无能为力,我们试着通过自定义校验来解决这个问题。

场景:要校验showStatus的0/1状态,可以用正则,但我们可以利用其他方式解决复杂场景。比如我们想要下面的场景

@NotNull(groups = {AddGroup.class, UpdateStatusGroup.class})
@ListValue(vals = {0,1}, groups = {AddGroup.class, UpdateGroup.class, UpdateStatusGroup.class})
private Integer showStatus;

添加依赖


    javax.validation
    validation-api
    2.1.0.Final


    org.hibernate
    hibernate-validator
    5.4.1.Final



    org.glassfish
    javax.el
    3.0.1-b08

1、自定义校验注解

必须有3个属性:

message()错误信息
groups()分组校验
payload()自定义负载信息

// 自定义注解
@documented
@Constraint(validatedBy = { ListValueConstraintValidator.class}) // 校验器 关联校验器和校验注解
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }) // 哪都可以标注
@Retention(RUNTIME)
public @interface ListValue {
    // 使用该属性去Validation.properties中取
    String message() default "{com.atguigu.common.valid.ListValue.message}";
    
    Class[] groups() default { };
    Class[] payload() default { };

    // 数组,需要用户自己指定
    int[] value() default {};
}
因为上面的message值对应的最终字符串需要去ValidationMessages.properties中获得,所以我们在common中新建文件ValidationMessages.properties

文件内容:
com.atguigu.common.valid.ListValue.message=必须提交指定的值 [0,1]

2、自定义校验器ConstraintValidator
上面只是定义了异常消息,但是怎么验证是否异常还没说,下面的ConstraintValidator就是说的

比如我们要限定某个属性值必须在一个给定的集合里,那么就通过重写initialize()方法,指定可以有哪些元素。

而controller接收到的数据用isValid(验证

public class ListValueConstraintValidator 
   implements ConstraintValidator { //<注解,校验值类型>
// 存储所有可能的值
private Set set=new HashSet<>();

@Override // 初始化,你可以获取注解上的内容并进行处理
public void initialize(ListValue constraintAnnotation) {
   // 获取后端写好的限制
   // 这个value就是ListValue里的value,我们写的注解是@ListValue(value={0,1})
   int[] value = constraintAnnotation.value();
   for (int i : value) {
       set.add(i);
   }
}

@Override // 覆写验证逻辑
public boolean isValid(Integer value, ConstraintValidatorContext context) {
   // 看是否在限制的值里
   return  set.contains(value);
}
}

具体的校验类需要实现ConstraintValidator接口,第一个泛型参数是所对应的校验注解类型,第二个是校验对象类型。在初始化方法initialize中,我们可以先做一些别的初始化工作,例如这里我们获取到注解上的value并保存下来,然后生成set对象。

真正的验证逻辑由isValid完成,如果传入形参的属性值在这个set里就返回true,否则返回false

3、关联校验器和校验注解

@Constraint(validatedBy = { ListValueConstraintValidator.class})

一个校验注解可以匹配多个校验器

4、使用实例

@ListValue(value = {0,1},groups ={AddGroup.class})
private Integer showStatus;

如验证手机号格式,可以参考https://blog.csdn.net/GAMEloft9/article/details/81699500

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5708414.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存
{script} {script}