2013-07-15 7 views
15

Sto scrivendo il mio primo processore Annotations e ho problemi con qualcosa che sembra banale ma non riesco a trovare alcuna informazione a riguardo.Ottieni la classe del campo nel processore delle annotazioni

Ho un elemento annotato con la mia annotazione

@MyAnnotation String property; 

Quando torno a questa proprietà come un elemento a mio processore non riesco a ottenere il tipo di elemento in alcun modo. In questo caso, si vorrebbe ottenere un'istanza Class o TypeElement che rappresenta String.

Ho provato a creare un'istanza di un oggetto di classe del tipo di contenitore con Class.forName() ma ha generato ClassNotFoundException. Penso che questo sia dovuto al fatto che non ho accesso al caricatore di classi che contiene la classe?

+0

perché non usi 'Field.getType()'? – Desert

+1

perché non ho accesso a un'istanza Field nelle mie annotazioni precessore per quanto ne so –

risposta

45

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:

  1. 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.
  2. 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); 
+0

Molto approfondito! grazie :) Mi sono perso specificando il motivo per cui volevo questo. Voglio ottenere il tipo di campo per ottenere annotazioni su quel tipo. Ma questo non sembra che sia possibile? –

+0

@ EmilSjölander Guarda le mie modifiche – Brian

+0

grazie mille! Estremamente ben scritto –

Problemi correlati