У меня есть поле 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();
}
Я скомпилировал проект несколько раз, процессор никогда не видит значения. Что я здесь упускаю? Процессор, очевидно, может видеть саму аннотацию, но передаваемые ему параметры скрыты.
И вы создаете аннотацию до, читая ее как зеркало? Ожидается, что это произойдет не только в одном и том же проходе, но и в том же правильном порядке? Я уверен, что это никогда не сработает в обратном порядке, и думаю, что маловероятно, что это можно было бы сделать даже в одном и том же проходе (поскольку в аннотации еще даже нет «полей», по крайней мере, не статического типа, поэтому значения карты не могут иметь тип, даже если они используются в источниках).
@ColinAlworth Ваш первый комментарий верен - класс аннотаций создается в том же раунде/проходе в одном и том же процессоре. Я создаю классы аннотаций, прежде чем читать их как зеркало, и этот порядок определен. В настоящее время это происходит в том же раунде, поскольку класс, использующий аннотацию, не создается и, следовательно, не присутствует в последующих раундах.
Мы не в состоянии быть здесь на 100% уверены, но я понимаю, что в течение раунда сгенерированные источники не будут скомпилированы, а вместо этого вам нужно дождаться окончания раунда, а до этого поля для этих вновь испущенных типов будет недоступен, даже в таких случаях. Ожидание следующего раунда не означает, что тип не будет присутствовать в раунде, просто он не будет предлагаться напрямую, вы все равно можете прочитать его из объекта Elements и т. д. См. пример auto-common BasicAnnotationProcessor , намеренно избегая обработки незавершенных классов, пока они не будут готовы.
@ColinAlworth Вероятно, вам следует опубликовать это как ответ :) annotation.toString() просто выводит буквальную аннотацию, и ему все равно, существует ли тип или где он находится. А вот тип Annotation (и его методы) видимо нужен для определения передаваемых параметров (правда не понимаю зачем). Использование BasicAnnotationProcessor для отсрочки обработки аннотаций решило проблему.




Используйте 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()), чтобы найти интересующую вас аннотацию.
Я не могу использовать field.getAnnotation(annotationClass), потому что аннотация, о которой идет речь, создается. Таким образом, у меня нет доступа к объекту класса во время компиляции процессора. Ваш второй пример ничего не выводит для меня (потому что карта почему-то пуста).
@Namnodorel это также работает для меня в сгенерированном классе, вы можете поделиться с нами реализацией аннотации.
@Namnodorel ты пробовал использовать @Retention(RetentionPolicy.RUNTIME) вместо SOURCE?
У меня сейчас есть, и нет никакой разницы. Хотя было бы очень странно, если бы это было так, учитывая, что весь смысл SOURCE должен использоваться процессорами аннотаций.
Интересно, что сохранение = ИСТОЧНИК означает, как и следовало ожидать, что аннотация присутствует только в файле .java, а не в файле .class. Это по-прежнему влияет на обработчики аннотаций двумя способами: во-первых, если ваш компилятор (eclipse, gradle и т. д.) является инкрементным, он может не выполнять повторный анализ всех источников, а вместо этого использовать байт-код, а во-вторых, если вы в конечном итоге ссылаетесь на аннотацию в отдельном модуль, то он уже скомпилирован, поэтому аннотация, конечно, будет отсутствовать. ИСТОЧНИК имеет смысл только тогда, когда вы на 100% уверены, что процессор должен видеть его только тогда, когда этот конкретный файл изменяется, что меньше, чем вы думаете.
@ColinAlworth Интересно ... Хотя, если бы это было проблемой, аннотация вообще не распознавалась бы, вместо того, чтобы просто не иметь элементов, или будет?
Хорошая точка зрения. Я думаю, нам нужно увидеть больше вашего кода, если этот ответ не решит вашу проблему, потому что я также успешно использовал обе стратегии в этом ответе.
@ColinAlworth Когда вы их использовали, вы создавали аннотацию? Я думаю, что это как-то связано с проблемой - хотя AnnotationMirror должен работать с исходным кодом, а не с байт-кодом.
Что вы подразумеваете под созданием аннотации? Другой процессор создает аннотацию на лету, а затем читает ее, когда вы ее компилируете? Или просто сгенерировать использование аннотации (я определенно сделал это). Обратите внимание, что до тех пор, пока код не будет успешно скомпилирован, некоторые части будут отсутствовать — «ErrorType» и другие способы определить это. Я думаю, вам следует добавить больше деталей к вашему вопросу, чтобы мы могли лучше помочь.
@ColinAlworth Сам класс Annotation создается (и записывается) в том же процессоре, но до доступа к любому аннотированному полю. Я включил это в вопрос, но, видимо, слишком двусмысленно. попробую перефразировать.
Спасибо, мне жаль, что я не понял этого раньше - я думаю, что это, вероятно, самая важная часть вопроса, и что вы, возможно, захотите зайти так далеко, чтобы привести краткий пример того, как это делается, поскольку это в противном случае кажется довольно далеко в сорняках. Я подозреваю, что даже если сама аннотация еще не скомпилирована, нет возможности прочитать ее свойства, поэтому вам нужно либо разбить это на несколько проходов одного и того же процессора, либо несколько процессоров...
Напомним, что обработчики аннотаций запускаются как часть компилятора в шагах, называемых «раундами». Этот процесс выполняется итеративно до тех пор, пока не останется нового кода для компиляции, а затем процессоры получат последний шанс запуститься (не обязательно для этого ответа, но полезно для большего контекста). В каждом раунде только вновь созданные типы напрямую передаются процессору для проверки.
Кажется, что здесь происходит то, что в течение в раунде вы создаете новый тип аннотации, который должен позволить процессору наблюдать определенные функции в некотором коде, отправленном для компиляции. Однако любые типы, созданные в течение данного раунда, еще не скомпилированы до начала следующего раунда.
По этому вопросу мы сталкиваемся здесь с конфликтом - скомпилированы некоторые исходники Java, которые используют аннотацию которого еще не существует. Процессор сначала создает аннотацию, а затем пытается прочитать вновь созданную аннотацию из этих частично скомпилированных источников. К сожалению, пока аннотация не будет скомпилирована, мы не можем ее прочитать. Вместо этого нам нужно дождаться следующего раунда (после компиляции самой аннотации), затем вернуться к тому классу, компиляция которого завершена, и изучить его.
Это можно реализовать самостоятельно без особых проблем, но чаще всего проще всего положиться на проект google/auto (в частности, на библиотеку auto-common, см. https://github.com/google/auto/tree/master/common) и расширить их класс BasicAnnotationProcessor. Одной из приятных функций, которые он поддерживает, является автоматическая проверка типов и проверка наличия каких-либо проблем с компиляцией - если они есть, они откладываются до более позднего раунда, чтобы вы могли справиться с ними без каких-либо проблем с разрешением типов.
Чтобы еще раз подтвердить вопрос - аннотация создается, а аннотированный тип - нет? Итак, до компиляции исходный код существовал с несуществующей аннотацией, а затем была создана аннотация в том же проходе, что вы прочитали в свойствах аннотации? Или это разные проходы или разные процессоры?