Quando si esegue il proprio processore di annotazione, non si ha accesso alle classi compilate. Il punto di elaborazione delle annotazioni è che si verifica prima della compilazione.
Invece, è necessario creare un processore di annotazione che gestisca in modo specifico il tipo di annotazione, quindi utilizzare l'API mirror per accedere al campo. Per esempio:
@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;
}
}
Per la convalida, è possibile non utilizzare tutte le classi che devono ancora essere compilati (inclusi quelli che stanno per essere compilato con l'annotazione) usando qualcosa come MyType.class
. Per questi, è necessario utilizzare solo le stringhe . Questo perché l'elaborazione delle annotazioni avviene durante una fase di pre-compilazione nota come "generazione di origine", che è ciò che consente di generare codice sorgente prima dell'esecuzione del compilatore tramite le annotazioni.
Un convalida esempio verificare che il tipo di campo è java.lang.String
(che è già compilato):
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);
}
}
Risorse
Edit:
Voglio ottenere il tipo di campo per ottenere le annotazioni su quel tipo. Ma questo non sembra che sia possibile?
In effetti è possibile!Questo può essere fatto utilizzando più metodi sulla 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();
Da qui, si hanno due scelte:
- Se l'annotazione che si sta cercando di trovare è già compilato (vale a dire che è da un'altra libreria) quindi puoi fare riferimento direttamente alla classe per ottenere l'annotazione.
- Se l'annotazione che stai cercando di trovare non è compilata (vale a dire è in fase di compilazione nella chiamata corrente a
javac
che sta eseguendo l'APT), puoi fare riferimento alle istanze AnnotationMirror
.
già compilato
DifferentAnnotation diffAnn = fieldTypeElement.getAnnotation(
DifferentAnnotation.class);
// Process diffAnn
molto straight-forward, questo ti dà l'accesso diretto al l'annotazione stessa.
Non compilato
Si noti che questa soluzione funziona indipendentemente dal fatto o meno l'annotazione viene compilato, è solo non pulite come il codice di cui sopra.
Qui ci sono un paio di metodi che ho scritto una volta di estrarre un certo valore da uno specchio di annotazione con il suo nome di classe:
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;
}
Diciamo che si sta cercando il DifferentAnnotation
annotazione e il codice sorgente sembra questo:
@DifferentAnnotation(name = "My Class")
public class MyClass {
@MyAnnotation
private String field;
// ...
}
questo codice stamperà My Class
:
String diffAnnotationName = findAnnotationValue(fieldTypeElement,
"com.example.DifferentAnnotation", "name", String.class);
System.out.println(diffAnnotationName);
perché non usi 'Field.getType()'? – Desert
perché non ho accesso a un'istanza Field nelle mie annotazioni precessore per quanto ne so –