Как получить доступ к полю, описанному в свойстве аннотации
Можно ли получить доступ к значению поля, где имя поля описано в аннотации, которая аннотирует другое поле в классе.
Например:
@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();
}
Теперь можно ли получить доступ к паролю поля из класса User в классе реализации ConstraintValidator?
Редактировать:
Я написал что-то вроде этого:
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 и сравнить значения полей?
3 ответа
Вам нужно либо написать ограничение уровня класса, в котором вы получите полный экземпляр User, переданный в вызов isValid, либо вы можете использовать что-то вроде @ScriptAssert.
На данный момент невозможно получить доступ к экземпляру корневого компонента как часть "нормальной" проверки поля. Существует проблема BVAL - BVAL-237, в которой обсуждается добавление этой функциональности, но пока она еще не является частью спецификации Bean Validation.
Обратите внимание, что существуют веские причины, по которым корневой бин недоступен для банкомата. Ограничения, которые полагаются на доступность корневого компонента, не будут выполнены в случае validateValue.
@ Харди Тенкс за чаевые. Наконец написал некоторый код, который соответствует (более или менее) ожидаемому результату.
Я вставлю это сюда, возможно, поможет кому-то решить его проблему.
@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;
}
Взломайте ... Взломайте, потому что эти внутренние реализации Hibernate не будут работать при переходе на модули Java.
@Override
public boolean isValid(final Serializable value, final ConstraintValidatorContext context) {
final var contextImpl = (ConstraintValidatorContextImpl) context;
//noinspection unchecked
final var descriptorImpl = (ConstraintDescriptorImpl<Exists>) contextImpl.getConstraintDescriptor();
final var existsAnnotation = descriptorImpl.getAnnotationDescriptor().getAnnotation();
// Removed b/c irrelevant
}