Java Bean 验证混淆了多个约束注释
Multiple constraint annotations confused on Java Bean Validation
我对一个字段上有多个约束注释的情况感到困惑,如下:
public class Student
{
@NotNull
@Size(min = 2, max = 14, message = "The name '${validatedValue}' must be between {min} and {max} characters long")
private String name;
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
}
测试用例:
public class StudentTest
{
private static Validator validator;
@BeforeClass
public static void setUp()
{
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
System.out.println(Locale.getDefault());
}
@Test
public void nameTest()
{
Student student = new Student();
student.setName(null);
Set<ConstraintViolation<Student>> constraintViolations = validator.validateProperty(student, "name");
System.out.println(constraintViolations.size());
System.out.println(constraintViolations.iterator().next().getMessage());
}
}
结果是:
1
Can't be null
即当违反@NotNull约束时,不会继续。是的,这是正确的情况。当一个检查失败时,我们不希望它检查下一个约束。但是当我使用自定义约束时情况就不同了。
我定义了两个自定义约束 ACheck 和 BCheck。
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = { ACheckValidator.class })
public @interface ACheck
{
String message() default "A check error";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = { BCheckValidator.class })
public @interface BCheck
{
String message() default "B check error";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
public class ACheckValidator implements ConstraintValidator<ACheck, String>
{
public void initialize(ACheck constraintAnnotation)
{
}
public boolean isValid(String value, ConstraintValidatorContext context)
{
return false;
}
}
public class BCheckValidator implements ConstraintValidator<BCheck, String>
{
public void initialize(BCheck constraintAnnotation)
{
}
public boolean isValid(String value, ConstraintValidatorContext context)
{
return false;
}
}
没有关于自定义约束的具体信息,我更改了 Student.java 并像这样使用自定义约束:
@ACheck
@BCheck
private String name;
再次测试,结果为:
2
B check error
也就是说,当@ACheck 约束被违反时,它也会检查@BCheck,为什么会这样,还有什么我忽略的吗?
when the @NotNull constraint is violated, it will not continue
这是不正确的。它将继续检查所有其他约束。只是 Size 验证器将 null 值视为可接受的值。原因是通常,您想要
- 一个非空的最小大小值:然后你应用两个约束
- 或可为 null 的值,如果存在该值,则必须具有最小大小:那么您仅应用 Size。
您误解了那些验证器 - they have no guarantee of order 在其中对其进行评估。
By default, constraints are evaluated in no particular order, regardless of which groups they belong to.
所以这意味着您的 ACheck
或 您的 BCheck
可能失败了,或者两者都失败了;不确定哪个故障会先发生。
如果您希望能够使用两个不同的注释来定义排序,则必须使用 @GroupSequence
来指定。
或者,如果您想快速失败,那么 configure the validator to do so。
Validator validator = Validation.byProvider( HibernateValidator.class )
.configure()
.failFast( true )
.buildValidatorFactory()
.getValidator();
我个人不鼓励这种方法,因为它意味着验证失败的用户必须在每次出现错误时重复请求资源,而不是得到一切前面写错了。
我对一个字段上有多个约束注释的情况感到困惑,如下:
public class Student
{
@NotNull
@Size(min = 2, max = 14, message = "The name '${validatedValue}' must be between {min} and {max} characters long")
private String name;
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
}
测试用例:
public class StudentTest
{
private static Validator validator;
@BeforeClass
public static void setUp()
{
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
System.out.println(Locale.getDefault());
}
@Test
public void nameTest()
{
Student student = new Student();
student.setName(null);
Set<ConstraintViolation<Student>> constraintViolations = validator.validateProperty(student, "name");
System.out.println(constraintViolations.size());
System.out.println(constraintViolations.iterator().next().getMessage());
}
}
结果是:
1
Can't be null
即当违反@NotNull约束时,不会继续。是的,这是正确的情况。当一个检查失败时,我们不希望它检查下一个约束。但是当我使用自定义约束时情况就不同了。
我定义了两个自定义约束 ACheck 和 BCheck。
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = { ACheckValidator.class })
public @interface ACheck
{
String message() default "A check error";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = { BCheckValidator.class })
public @interface BCheck
{
String message() default "B check error";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
public class ACheckValidator implements ConstraintValidator<ACheck, String>
{
public void initialize(ACheck constraintAnnotation)
{
}
public boolean isValid(String value, ConstraintValidatorContext context)
{
return false;
}
}
public class BCheckValidator implements ConstraintValidator<BCheck, String>
{
public void initialize(BCheck constraintAnnotation)
{
}
public boolean isValid(String value, ConstraintValidatorContext context)
{
return false;
}
}
没有关于自定义约束的具体信息,我更改了 Student.java 并像这样使用自定义约束:
@ACheck
@BCheck
private String name;
再次测试,结果为:
2
B check error
也就是说,当@ACheck 约束被违反时,它也会检查@BCheck,为什么会这样,还有什么我忽略的吗?
when the @NotNull constraint is violated, it will not continue
这是不正确的。它将继续检查所有其他约束。只是 Size 验证器将 null 值视为可接受的值。原因是通常,您想要
- 一个非空的最小大小值:然后你应用两个约束
- 或可为 null 的值,如果存在该值,则必须具有最小大小:那么您仅应用 Size。
您误解了那些验证器 - they have no guarantee of order 在其中对其进行评估。
By default, constraints are evaluated in no particular order, regardless of which groups they belong to.
所以这意味着您的 ACheck
或 您的 BCheck
可能失败了,或者两者都失败了;不确定哪个故障会先发生。
如果您希望能够使用两个不同的注释来定义排序,则必须使用 @GroupSequence
来指定。
或者,如果您想快速失败,那么 configure the validator to do so。
Validator validator = Validation.byProvider( HibernateValidator.class )
.configure()
.failFast( true )
.buildValidatorFactory()
.getValidator();
我个人不鼓励这种方法,因为它意味着验证失败的用户必须在每次出现错误时重复请求资源,而不是得到一切前面写错了。