Ответ 1
При запуске вашего обработчика аннотаций у вас нет доступа к скомпилированным классам. Точка обработки аннотаций заключается в том, что она выполняется с предварительным компиляцией.
Вместо этого вам нужно создать обработчик аннотации, который специально обрабатывает ваш тип аннотации, а затем использовать зеркальный API для доступа к полю. Например:
@SupportedAnnotationTypes("com.example.MyAnnotation")
public class CompileTimeAnnotationProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
// Only one annotation, so just use annotations.iterator().next();
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(
annotations.iterator().next());
Set<VariableElement> fields = ElementFilter.fieldsIn(elements);
for (VariableElement field : fields) {
TypeMirror fieldType = field.asType();
String fullTypeClassName = fieldType.toString();
// Validate fullTypeClassName
}
return true;
}
}
Для проверки вы не можете использовать любые классы, которые еще не скомпилированы (включая те, которые собираются компилироваться с помощью аннотации), используя что-то вроде MyType.class
. Для этого вы должны использовать только строки. Это связано с тем, что обработка аннотаций происходит во время фазы предварительной компиляции, известной как "генерация источника", что позволяет генерировать исходный код перед запуском компилятора с помощью аннотаций.
Пример проверки, подтверждающий, что тип поля java.lang.String
(который уже скомпилирован):
for (VariableElement field : fields) {
TypeMirror fieldType = field.asType();
String fullTypeClassName = fieldType.toString();
if (!String.class.getName().equals(fullTypeClassName)) {
processingEnv.getMessager().printMessage(
Kind.ERROR, "Field type must be java.lang.String", field);
}
}
Ресурсы
- Главная страница APT
- Зеркальный API Javadocs (Java 7 и старше)
- Изменить: Mirror API Javadocs (Java 8)
- Обратите внимание, что теперь API-интерфейс зеркала стандартизован в Java 8 под
javax.lang.model
, а старый API устарел. См. этот пост в блоге для получения дополнительной информации. Если вы используете классыjavax
, вам не нужно беспокоиться.
- Обратите внимание, что теперь API-интерфейс зеркала стандартизован в Java 8 под
Edit:
Я хочу, чтобы тип поля получал аннотации этого типа. Но это не похоже, что это будет возможно?
В самом деле, это возможно! Это можно сделать, используя больше методов на TypeMirror
:
if (fieldType.getKind() != TypeKind.DECLARED) {
processingEnv.getMessager().printMessage(
Kind.ERROR, "Field cannot be a generic type.", field);
}
DeclaredType declaredFieldType = (DeclaredType) fieldType;
TypeElement fieldTypeElement = (TypeElement) declaredFieldType.asElement();
Здесь у вас есть два варианта:
- Если аннотация, которую вы пытаетесь найти, уже скомпилирована (т.е. из другой библиотеки), вы можете напрямую ссылаться на класс, чтобы получить аннотацию.
- Если аннотация, которую вы пытаетесь найти, не скомпилирована (т.е. она компилируется в текущем вызове
javac
, которая запускает APT), вы можете ссылаться на нее через экземплярыAnnotationMirror
.
Уже скомпилировано
DifferentAnnotation diffAnn = fieldTypeElement.getAnnotation(
DifferentAnnotation.class);
// Process diffAnn
Очень прямолинейно, это дает вам прямой доступ к самой аннотации.
Не скомпилировано
Обратите внимание, что это решение будет работать независимо от того, компилируется компиляция или нет, это просто не так чисто, как код выше.
Вот несколько методов, которые я написал один раз, чтобы извлечь определенное значение из зеркала аннотации по имени его класса:
private static <T> T findAnnotationValue(Element element, String annotationClass,
String valueName, Class<T> expectedType) {
T ret = null;
for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) {
DeclaredType annotationType = annotationMirror.getAnnotationType();
TypeElement annotationElement = (TypeElement) annotationType
.asElement();
if (annotationElement.getQualifiedName().contentEquals(
annotationClass)) {
ret = extractValue(annotationMirror, valueName, expectedType);
break;
}
}
return ret;
}
private static <T> T extractValue(AnnotationMirror annotationMirror,
String valueName, Class<T> expectedType) {
Map<ExecutableElement, AnnotationValue> elementValues = new HashMap<ExecutableElement, AnnotationValue>(
annotationMirror.getElementValues());
for (Entry<ExecutableElement, AnnotationValue> entry : elementValues
.entrySet()) {
if (entry.getKey().getSimpleName().contentEquals(valueName)) {
Object value = entry.getValue().getValue();
return expectedType.cast(value);
}
}
return null;
}
Скажем, что вы ищете аннотацию DifferentAnnotation
, и ваш исходный код выглядит следующим образом:
@DifferentAnnotation(name = "My Class")
public class MyClass {
@MyAnnotation
private String field;
// ...
}
Этот код напечатает My Class
:
String diffAnnotationName = findAnnotationValue(fieldTypeElement,
"com.example.DifferentAnnotation", "name", String.class);
System.out.println(diffAnnotationName);