为何validator.getConstraintsForClass获取@CustomPassword约束时min值未被覆盖
我自定义了多个Java校验注解用于POJO字段标注,其中@CustomPassword基于@CustomString扩展。尝试通过@OverridesAttribute(constraint = CustomString.class, name = "min")将@CustomPassword的min默认值设为10,实现注解属性的层级覆盖,但调用validator.getConstraintsForClass获取@CustomPassword的约束时,发现@CustomString的min值仍是默认的0,而非预期的10。
相关代码如下:
@CustomString 注解定义
import java.lang.annotation.*; import javax.validation.Constraint; import javax.validation.OverridesAttribute; import javax.validation.Payload; import javax.validation.constraints.Size; @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE}) @Size @Constraint(validatedBy = {}) public @interface CustomString { @OverridesAttribute(constraint = Size.class, name = "min") int min() default 0; @OverridesAttribute(constraint = Size.class, name = "max") int max() default Integer.MAX_VALUE; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; String message() default "{org.example.annotation.base.string}"; }
@CustomPassword 注解定义
import org.example.annotation.base.CustomString; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import javax.validation.Constraint; import javax.validation.OverridesAttribute; import javax.validation.Payload; @CustomString @Constraint(validatedBy = {}) @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE}) public @interface CustomPassword { @OverridesAttribute(constraint = CustomString.class, name = "min") int min() default 10; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; String message() default "{org.example.annotation.extended.password}"; }
测试代码
Class<?> klass = CustomPassword.class; Validator validator = Validation.byDefaultProvider() .configure() .messageInterpolator(new ParameterMessageInterpolator()) .buildValidatorFactory().getValidator(); BeanDescriptor beanDescriptor = validator.getConstraintsForClass(klass);
核心原因
@OverridesAttribute的生效时机:该注解的属性覆盖逻辑仅在注解被应用到具体元素(如POJO字段)时才会被Validator解析生效,目的是为该元素生成实际的校验规则。直接读取注解类本身的元数据时,Validator不会自动合并层级覆盖的属性值。getConstraintsForClass的作用边界:这个方法是获取注解类自身的原始约束元数据,而非该注解被应用到元素时的最终生效约束。因此它会直接读取@CustomString的默认min=0,不会处理@CustomPassword中对它的覆盖配置。
验证覆盖逻辑是否生效的正确方式
要确认min=10的覆盖是否生效,需要将@CustomPassword标注到实际POJO字段上,再进行校验:
// 测试用POJO public class User { @CustomPassword private String password; // getter、setter 省略 } // 正确的测试代码 Validator validator = Validation.byDefaultProvider() .configure() .messageInterpolator(new ParameterMessageInterpolator()) .buildValidatorFactory().getValidator(); // 传入password长度小于10的User实例 User user = new User(); user.setPassword("123456789"); // 长度9,小于10 Set<ConstraintViolation<User>> violations = validator.validate(user); // 此时会产生约束违规,说明min=10的规则已生效
若需读取注解类层级覆盖后的属性值
如果确实需要在注解类层面获取合并后的属性值,无法通过Validator API直接实现,需要自行通过反射遍历注解的元注解,手动处理@OverridesAttribute的覆盖逻辑。
内容的提问来源于stack exchange,提问作者bobbyrne01

