Получить все значения сгенерированной аннотации в обработчике аннотаций - PullRequest
1 голос
/ 15 марта 2019

У меня есть поле VariableElement, аннотированное сгенерированной аннотацией (поэтому я не могу использовать field.getAnnotation(annotationClass)). Мне нужно получить все параметры, переданные этой аннотации.

Обратите внимание, что под «сгенерированной аннотацией» я подразумеваю, что буквально сам класс аннотации (а не аннотированный) был сгенерирован процессором аннотации. Аннотируемое поле / класс находится в рукописном исходном коде.

Не похоже, что это будет так сложно, до сих пор я придумал это:

for (AnnotationMirror annotation : field.getAnnotationMirrors()) {
    Map<? extends ExecutableElement, ? extends AnnotationValue> annotationValueMap = annotation.getElementValues();

    messager.printMessage(Diagnostic.Kind.WARNING, annotation.toString() + ":" + annotationValueMap.toString());
}

Я думал, что это сделает это, но вывод для поля следующий:

@MyAnnotation:{}

Итак, процессор распознает, что поле аннотировано, но я не могу получить доступ к переданным параметрам. Даже при том, что поле определенно аннотировано и передает параметры с аннотацией (это необходимо, поскольку аннотация определяет обязательные параметры и не имеет значений по умолчанию):

@MyAnnotation(max = 387, min = 66876, ...)
private Integer myField;

Вот сгенерированный код аннотации:

@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.FIELD)
public @interface MyAnnotation {
  int max();

  boolean allowAuto();

  int min();
}

Я несколько раз скомпилировал проект, процессор никогда не видит значения. Что я здесь пропускаю? Процессор, очевидно, может видеть саму аннотацию, но передаваемые ему параметры скрыты.

Ответы [ 2 ]

2 голосов
/ 18 марта 2019

Напомним, что процессоры аннотаций выполняются как часть компилятора, за этапы, называемые «раундами».Этот процесс выполняется итеративно, пока нет нового кода для компиляции, и затем процессоры получают последний шанс на запуск (не обязательно для этого ответа, но полезно для большего контекста).В каждом раунде только недавно созданные типы напрямую передаются процессору для проверки.

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

Для этого вопроса мы столкнулись с конфликтом здесь - некоторые источники Java скомпилированы, которые используют аннотацию , которая нееще не существует .Процессор сначала создает аннотацию, а затем пытается прочитать вновь созданную аннотацию из этих частично скомпилированных источников.К сожалению, пока аннотация не скомпилирована, мы не можем прочитать аннотацию.Вместо этого нам нужно подождать до следующего раунда (после того, как сама аннотация скомпилирована), затем вернуться к тому классу, который завершил компиляцию, и исследовать его.

Это можно реализовать самостоятельно без особых проблем,но самым простым способом часто является использование проекта google / auto (в частности, библиотеки auto-common, см. https://github.com/google/auto/tree/master/common), и расширение их класса BasicAnnotationProcessor. Одна из приятных функций, которые он поддерживает, заключается в автоматическом исследовании типов ипроверьте, есть ли какие-либо проблемы компиляции - если так, они откладываются до более позднего раунда, чтобы вы могли обработать их без проблем с разрешением типов.

1 голос
/ 15 марта 2019

Используйте getAnnotation(MyAnnotation.class) доступно из VariableElement

в вашем примере кода, вы можете сделать это, чтобы получить min и max параметры

MyAnnotation myAnnotation= field.getAnnotation(MyAnnotation.class);
int max = myAnnotation.max();
int min = myAnnotation.min();

, это будет работать, если только аннотациичлены возвращают class/class[] значение, в котором вы получите исключение, если попытаетесь получить значение с помощью этого метода.

Подробнее о том, как получить значения литералов класса, можно найти в этом ответе

Как прочитать значения Class [] из вложенной аннотации в процессоре аннотаций

Или с помощью зеркал аннотации

for (AnnotationMirror annotation : field.getAnnotationMirrors()) {
    Map<? extends ExecutableElement, ? extends AnnotationValue> annotationValueMap = annotation.getElementValues();
    annotationValueMap.forEach((element, annotationValue) -> {
        messager.printMessage(Diagnostic.Kind.WARNING, element.getSimpleName().toString() + ":" + annotationValue.getValue());
    });
}

В случае, если у вас более одной аннотациина поле вы можете перебирать зеркала аннотаций и использовать проверку types.isSameType(annotationMirror.getAnnotationType(), elements.getTypeElement(MyAnnotation.class.getName()).asType()), чтобы найти интересующую вас аннотацию

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