@Notnull具有多个字段的Spring Custom Annotation验证

时间:2017-03-21 20:56:36

标签: java spring annotations jsr

如何编写(类型级别注释)自定义注释以允许选择验证(许多属性中的一个必须不为空)?

1 个答案:

答案 0 :(得分:4)

即使这个“问题”非常广泛,我也会给出答案,因为我在这里有完整的代码:

@Target(ElementType.TYPE)
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = ChoiceValidator.class)
public @interface Choice {
    String[] value();

    boolean multiple() default false;

    String message() default "{com.stackoverflow.validation.Choice.message}";

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

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

这是一个使用bean属性访问的验证器:

public class ChoiceValidator implements ConstraintValidator<Choice, Object> {
    private String[] properties;
    private boolean allowMultiple;

    @Override
    public void initialize(Choice constraintAnnotation) {
        if (constraintAnnotation.value().length < 2) {
            throw new IllegalArgumentException("at least two properties needed to make a choice");
        }
        properties = constraintAnnotation.value();
        allowMultiple = constraintAnnotation.multiple();
    }

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
        try {
            BeanInfo info = getBeanInfo(value.getClass());
            long notNull = Stream.of(properties)
                    .map(property -> Stream.of(info.getPropertyDescriptors())
                            .filter(desr -> desr.getName().equals(property))
                            .findAny()
                            .orElse(null)
                    )
                    .map(prop -> getProperty(prop, value))
                    .filter(Objects::nonNull)
                    .count();
            return allowMultiple ? notNull != 0 : notNull == 1;
        } catch (IntrospectionException noBean) {
            return false;
        }
    }

    private Object getProperty(PropertyDescriptor prop, Object bean) {
        try {
            return prop.getReadMethod() == null ? null : prop.getReadMethod().invoke(bean);
        } catch (ReflectiveOperationException noAccess) {
            return null;
        }
    }
}

典型用法可能如下所示( lombok 注释生成getter和setter):

@Data
@Choice({"one", "two"})
class OneOf {
    private String one;
    private String two;
    private String whatever;
}

@Data
@Choice(value = {"one", "two"}, multiple = true)
class AnyOf {
    private String one;
    private String two;
}

但要澄清一下:Stackoverflow是一个QA社区,供开发人员交流知识。它不是一个问“你可以免费为我编码吗?”的地方。所有有价值的贡献者至少先尝试解决问题,然后再提出详细的问题。回答问题的人会利用他们的业余时间而不需要付钱。请通过询问具体问题并展示自己未来的努力来表示尊重。