当前位置: 首页 > 工具软件 > OVal > 使用案例 >

Java校验框架使用@Valid、@Validated、OVAL+Groovy

张献
2023-12-01

@Valid 与 @Validated

Spring Validation验证框架对参数的验证机制提供了@Validated;

javax提供了@Valid,配合BindingResult可以直接提供参数验证结果;

检验Controller入参规范时,@Validated和@Valid在基本验证功能上没有太多区别。但在分组、注解地方、嵌套验证等功能上有所不同:

1. 分组
@Validated:提供分组功能,可以在入参验证时,根据不同的分组采用不同的验证机制。
@Valid:没有分组功能。

2. 注解
@Validated:用在类型、方法和方法参数上。但不能用于成员属性(field)。
@Valid:可以用在方法、构造函数、方法参数和成员属性(field)上。

3. 嵌套验证
一个待验证的pojo类,其中还包含了待验证的对象,需要在待验证对象上注解@valid,才能验证待验证对象中的成员属性。

@Validated分组校验示例:

假设小学生身高范围在【100, 150】,初中生身高范围在【130,170】

public class Student {

    //针对两种情况,新建对应接口类
    public interface Pupil{}
    public interface Junior{}

    @NotNull(message = "姓名不能为空")
    private String name;

    @Range(min = "100", max = "150", message = "小学生身高不在正常范围内", groups = {Pupil.class})
    @Range(min = "130", max = "170", message = "初中生身高不在正常范围内", groups = {Junior.class})
    private int height;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }
}

@PostMapping("/pupil")
public Object ulti (@Validated(Student.Pupil.class) @RequestBody Student person) {
	return person;
}

特殊情况下,如果@Range里的min和max要求在配置中心进行配置,更灵活机动,就需求自定义校验注解@MyRange

###配置中心
student:
  height:
    #小学
    pupil:
      min: 100
      max: 150
    #初中
    junior:
      min: 150
      max: 180

----------------------------------------------------------

public class Student {

    public interface Pupil{}
    public interface Junior{}

    @NotNull(message = "姓名不能为空")
    private String name;

    @MyRange(min = "student.height.pupil.min", max = "student.height.pupil.max", message = "小学生身高不在正常范围内", groups = {Pupil.class})
    @MyRange(min = "student.height.junior.min", max = "student.height.junior.max", message = "初中生身高不在正常范围内", groups = {Junior.class})
    private String height;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getHeight() {
        return height;
    }

    public void setHeight(String height) {
        this.height = height;
    }
}

--------------------------------------------------------------------
###自定义校验注解
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Documented
@Repeatable(MyRange.List.class)
@Constraint(validatedBy = {MyRangeValidator.class})
public @interface MyRange {

    String message() default "{数值范围校验}";

    /**
     * @return value the element must be higher or equal to
     */
    String min();
    String max();

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
    @Retention(RUNTIME)
    @Documented
    @interface List {
        MyRange[] value();
    }
}

------------------------------------------------------------
@Component
@RefreshScope
public class RangeValidator implements ConstraintValidator<Range, String> {

    @Resource
    private Environment environment;

    private String minStr = "";
    private String maxStr = "1";

    @Override
    public void initialize(Range constraintValidator) {
        minStr = constraintValidator.min();
        maxStr = constraintValidator.max();
    }

    // 这里必须要实现 isValid(),校验逻辑就是这里实现
    @Override
    public boolean isValid(String valStr, ConstraintValidatorContext constraintValidatorContext) {
        if (valStr == null || "".equals(valStr)) {
            return false;
        }
        //判断数值字符串是否都是数值,非负整数
        final String regex = "^[1-9]\\d*|0$";
        boolean isMatch = Pattern.matches(regex, valStr);
        if (!isMatch) {
            //不是非负整数
            return false;
        }
        long min = 0L;
        long max = 9223372036854775807L;
        final int val = Integer.valueOf(valStr);
        try {
            min = Integer.valueOf(environment.getProperty(minStr));
            max = Integer.valueOf(environment.getProperty(maxStr));
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (min > val || val > max) {
            return false;
        }
        return true;
    }
}

通过groovy表达式拓展oval——实现根据同一实体中的其他属性值对某个字段进行校验

<dependency>
	<groupId>org.codehaus.groovy</groupId>
	<artifactId>groovy-all</artifactId>
	<version>3.0.9</version>
</dependency>

<!-- https://mvnrepository.com/artifact/net.sf.oval/oval -->
<dependency>
	<groupId>net.sf.oval</groupId>
	<artifactId>oval</artifactId>
	<version>3.2.1</version>
</dependency>
public class Student {

    @NotNull(message = "姓名不能为空")
    private String name;

    @net.sf.oval.constraint.Range(min = 10, max = 60, when = "groovy:_this.name=='jack'", message = "jack数据异常")
    @net.sf.oval.constraint.Range(min = 8, max = 100, when = "groovy:_this.name=='tom'", message = "tom数据异常")
    private String height;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getHeight() {
        return height;
    }

    public void setHeight(String height) {
        this.height = height;
    }
}

-------------------------------------------------------------------------

Validator validator = new Validator();
List<ConstraintViolation> messages = validator.validate(person);
StringBuilder sb = new StringBuilder();
messages.stream().forEach(message -> sb.append(message.getMessage()));


 类似资料: