Доступ к методу, к которому присоединен экземпляр аннотации для проверки перекрестных параметров

#java #validation #annotations #java-annotations

#java #проверка #аннотации #java-аннотации

Вопрос:

У меня есть экземпляр аннотации Java, который имеет цель МЕТОДА: возможно ли получить объект метода, к которому он присоединен?

Я пытаюсь выполнить проверку кросс-параметров, где валидатор будет применять логику к комбинации параметров, чтобы определить, верны ли они. Однако, поскольку средство проверки может применяться к нескольким методам, я ищу способ указать, какие параметры являются какими.

Например, в приведенном ниже примере у меня есть методы, которые используют два аргумента для указания диапазона по началу и концу. Я хочу, чтобы средство проверки проверяло, что начало не больше конца. Я хочу использовать аннотации к параметрам, чтобы указать, какой параметр является начальным, а какой конечным, а затем попросить средство проверки использовать эти аннотации, чтобы определить, какие параметры использовать при проверке.

 @Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface StartParam {}

/***************************/

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface EndParam {}

/***************************/

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = RangeParamsValidator.class)
@SupportedValidationTarget(ValidationTarget.PARAMETERS)
public @interface RangeParams { }

/***************************/

public class RangeParamsValidator implements ConstraintValidator<RangeParams, Object[]> {

    private int indexOfStartArg;
    private int indexOfEndArg;

    @Override
    public void initialize(
        RangeParams constraintAnnotation
    ) {
        // This is where I'm hoping to get the method that `constraintAnnotation`
        // is attached to, so I can iterate over its params and see which are
        // annotated with @StartParam and @EndParam so I can set indexOfStartArg
        // and indexOfEndArg;
    }

    @Override
    public boolean isValid(Object[] value, ConstraintValidatorContext context) {
        Integer start = value[indexOfStartArg];
        Integer end = value[indexOfEndArg];
        return start <= end;
    }
}


/***************************/

// Example use:

class Whatever {

    @RangeParams
    void doSomething(
        String ignoreThisArg,
        @StartParam int start,
        @FinishParam int finish,
        Object ignoreThisToo
    ) {
        // ...
    }
}
  

Ответ №1:

Я не смог понять это, используя имена параметров, хотя может быть способ. Но вот решение с положением параметров.

Сначала вам нужно создать свойства в ограничении, чтобы можно было передавать положение полей.

 @Constraint(validatedBy = RangeParamsValidator.class)
@Target( {ElementType.CONSTRUCTOR, ElementType.METHOD} )
@Retention(RetentionPolicy.RUNTIME)
public @interface RangeParams {
    String message() default "Constraint violated";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
    int indexOfStartArg();
    int indexOfEndArg();
}
  

message , groups и payload должны соответствовать стандарту (подробнее в документации). Важной частью являются 2 дополнительных параметра для предоставления индекса. Если вы установите значение по умолчанию, параметры будут необязательными.

Затем при инициализации средства проверки вы получаете значения.

 public class RangeParamsValidator implements ConstraintValidator<RangeParams, Object[]> {

private int indexOfStartArg;
private int indexOfEndArg;

@Override
public void initialize(RangeParams constraintAnnotation) {
    indexOfStartArg = constraintAnnotation.indexOfStartArg();
    indexOfEndArg = constraintAnnotation.indexOfEndArg();
}
...
  

И вы можете использовать значения по своему усмотрению.

Затем, чтобы указать значения, вы делаете это в аннотации следующим образом:

 @RangeParams(indexOfStartArg = 1, indexOfEndArg = 2)
void doSomething(
    String ignoreThisArg,
    int start,
    int finish,
    Object ignoreThisToo
) {
    // ...
}
  

Я думаю, что это можно сделать с именами параметров, поскольку в isValid() контексте есть список имен параметров. Но мне удалось получить доступ к значениям только через реализацию интерфейса, а не через сам интерфейс, поэтому мне нужно было бы добавить контекст к реализации, которая не кажется чистой.