【问题标题】:How to access a field which is described in annotation property如何访问注释属性中描述的字段
【发布时间】:2016-06-10 14:22:28
【问题描述】:

是否可以访问字段值,其中字段名称在注释中描述,该注释注释类中的另一个字段。

例如:

@Entity
public class User {

    @NotBlank
    private String password;

    @Match(field = "password")
    private String passwordConfirmation;
}

注释:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = FieldMatchValidator.class)
@Documented
public @interface Match {

    String message() default "{}";

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

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

    String field();
}

现在,是否可以从 ConstraintValidator 实现类中的 User 类中访问字段密码?


编辑:

我是这样写的:

public class MatchValidator implements ConstraintValidator<Match, Object> {

private String mainField;
private String secondField;
private Class clazz;

@Override
public void initialize(final Match match) {
    clazz = User.class;

    final Field[] fields = clazz.getDeclaredFields();
    for (Field field : fields) {
        if (field.isAnnotationPresent(Match.class)) {
            mainField = field.getName();
        }
    }

    secondField = match.field();
}

@Override
public boolean isValid(final Object value, final ConstraintValidatorContext constraintValidatorContext) {
    try {
        Object o; //Now how to get the User entity instance?

        final Object firstObj = BeanUtils.getProperty(o, mainField);
        final Object secondObj = BeanUtils.getProperty(o, secondField);

        return firstObj == null && secondObj == null || firstObj != null && firstObj.equals(secondObj);
    } catch (final Exception ignore) {
        ignore.printStackTrace();
    }
    return true;
}
}

现在的问题是如何获取 User 对象实例并比较字段值?

【问题讨论】:

  • @ursa 感谢您的提示,对我帮助很大。但现在我遇到了另一个问题,请参阅编辑。
  • @zach 你还没有对象吗? isValid方法的参数value
  • @Jesper value 参数是private String passwordConfirmation 属性的值。
  • 啊,是的,我明白了。我认为没有办法从验证器的 isValid 方法中获取包含属性的 User 对象。

标签: java reflection annotations hibernate-validator


【解决方案1】:

@Hardy 求小费。最后写了一些符合(或多或少)预期结果的代码。

我把它贴在这里,也许会帮助某人解决他的问题。

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Match {

    String field();

    String message() default "";
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = MatchValidator.class)
@Documented
public @interface EnableMatchConstraint {

    String message() default "Fields must match!";

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

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

public class MatchValidator implements  ConstraintValidator<EnableMatchConstraint, Object> {

    @Override
    public void initialize(final EnableMatchConstraint constraint) {}

    @Override
    public boolean isValid(final Object o, final ConstraintValidatorContext context) {
        boolean result = true;
        try {
            String mainField, secondField, message;
            Object firstObj, secondObj;

            final Class<?> clazz = o.getClass();
            final Field[] fields = clazz.getDeclaredFields();

            for (Field field : fields) {
                if (field.isAnnotationPresent(Match.class)) {
                    mainField = field.getName();
                    secondField = field.getAnnotation(Match.class).field();
                    message = field.getAnnotation(Match.class).message();

                    if (message == null || "".equals(message))
                        message = "Fields " + mainField + " and " + secondField + " must match!";

                    firstObj = BeanUtils.getProperty(o, mainField);
                    secondObj = BeanUtils.getProperty(o, secondField);

                    result = firstObj == null && secondObj == null || firstObj != null && firstObj.equals(secondObj);
                    if (!result) {
                        context.disableDefaultConstraintViolation();
                        context.buildConstraintViolationWithTemplate(message).addPropertyNode(mainField).addConstraintViolation();
                        break;
                    }
                }
            }
        } catch (final Exception e) {
            // ignore
            //e.printStackTrace();
        }
        return result;
    }
}

以及如何使用它...?像这样:

@Entity
@EnableMatchConstraint
public class User {

    @NotBlank
    private String password;

    @Match(field = "password")
    private String passwordConfirmation;
}

【讨论】:

    【解决方案2】:

    您要么需要编写一个class level constraint,在其中将完整的User 实例传递给isValid 调用,要么你可以使用@ScriptAssert 之类的东西。

    目前,作为“正常”字段验证的一部分,无法访问根 bean 实例。有一个 BVAL 问题 - BVAL-237 - 讨论了添加此功能,但到目前为止它还不是 Bean 验证规范的一部分。

    注意,根 bean 无法访问 atm 是有充分理由的。对于 validateValue 情况,依赖于可访问的根 bean 的约束将失败。

    【讨论】:

    • 你说我需要写类级别的约束,有没有可能使用@Entity类级别的注解而不是写新的注解?
    • 不确定您使用@Entity 的意思,但答案是否定的。您需要编写自己的客户类级别约束。
    【解决方案3】:

    Hack around... Hack 因为这些内部 Hibernate 实现在迁移到 Java 模块时将无法工作。

    @Override
    public boolean isValid(final Serializable value, final ConstraintValidatorContext context) {
        var context = (ConstraintValidatorContextImpl) context;
        //no-inspection unchecked
        var descriptor = (ConstraintDescriptorImpl<Exists>) context.getConstraintDescriptor();
        var existsAnnotation = descriptor.getAnnotationDescriptor().getAnnotation();
        // Removed b/c irrelevant
    }
    

    【讨论】:

    • 这很方便! +1
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-16
    相关资源
    最近更新 更多