2013-04-30 21 views
41

Ich versuche, einige Java-Annotationsmagie zu tun. Ich muss sagen, dass ich immer noch auf Annotationstricks eingehe und dass bestimmte Dinge für mich immer noch nicht klar sind.So erstellen Sie eine Instanz einer Annotation

Also ... Ich habe einige kommentierte Klassen, Methoden und Felder. Ich habe eine Methode, die Reflektion verwendet, um einige Prüfungen der Klassen durchzuführen und einige Werte in eine Klasse zu injizieren. Das alles funktioniert gut.

Allerdings stehe ich jetzt vor einem Fall, wo ich eine Instanz (sozusagen) einer Anmerkung benötigen. Also ... Anmerkungen sind nicht wie normale Schnittstellen und Sie können keine anonyme Implementierung einer Klasse durchführen. Ich verstehe es. Ich habe mich in einigen Beiträgen nach ähnlichen Problemen umgesehen, aber ich kann anscheinend nicht die Antwort auf das finden, wonach ich suche.

Ich würde im Grunde gerne eine Annotation bekommen und in der Lage sein, einige seiner Felder durch Reflektion zu setzen (nehme ich an). Gibt es überhaupt einen Weg dies zu tun?

+1

Sie können Anmerkungen nicht instanziieren oder ändern. Sie sind bereits vorhanden, wenn Sie den Code ausführen. Sie können sie nur abrufen. Können Sie einige Codebeispiele für das geben, was Sie tun möchten? – NilsH

+0

haben Sie versucht, getAnnotation() verfügbar in java.lang.Class/java.lang.reflect.Method –

+0

Haben Sie versucht getClass(). GetAnnotations()? – prasanth

Antwort

48

Nun, es ist scheinbar nichts kompliziertes. Wirklich!

Wie bereits von einem Kollegen aus, können Sie einfach eine anonyme Instanz der Anmerkung erstellen (wie jede Schnittstelle) wie folgt aus:

MyAnnotation:

public @interface MyAnnotation 
{ 

    String foo(); 

} 

Aufrufen Code:

class MyApp 
{ 
    MyAnnotation getInstanceOfAnnotation(final String foo) 
    { 
     MyAnnotation annotation = new MyAnnotation() 
     { 
      @Override 
      public String foo() 
      { 
       return foo; 
      } 

      @Override 
      public Class<? extends Annotation> annotationType() 
      { 
       return MyAnnotation.class; 
      } 
     }; 

     return annotation; 
    } 
} 

Danksagungen an Martin Grigorov.

+0

Hmm, sieht so aus, als müssten Sie auch ['annotationType'] implementieren (http://docs.oracle.com/javase/7/docs/api/java/lang/annotation/Annotation.html#) annotationTyp()). Vermutlich würde dies nur "MyAnnotation.class" zurückgeben. –

+0

Interessante Sache zu beachten. Ich hatte die IDE tatsächlich die Methoden generieren lassen und nicht bemerkt, dass es das auch implementiert hat. Es wurde "null" zurückgegeben und funktioniert. Ich habe es auf den Klassentyp der Anmerkungen festgelegt, obwohl ich nicht sicher bin, was es wirklich sein sollte. – carlspring

+0

Ich vermute, das ist für Annotation Lookup-Funktionalität erforderlich. Für Ihren speziellen Fall wird es wahrscheinlich nicht benötigt, aber es erscheint korrekter, das richtige zurückzugeben (da Sie es unabhängig implementieren müssen). –

7

Sie könnten einen Annotations-Proxy wie this one oder this one aus dem Hibernate Validator-Projekt verwenden (Disclaimer: Ich bin ein Committer der letzteren).

5

können Sie verwenden sun.reflect.annotation.AnnotationParser.annotationForMap(Class, Map):

public @interface MyAnnotation { 
    String foo(); 
} 

public class MyApp { 
    public MyAnnotation getInstanceOfAnnotation(final String foo) { 
     MyAnnotation annotation = AnnotationParser.annotationForMap(
      MyAnnotation.class, Collections.singletonMap("foo", "myFooResult")); 
    } 
} 

Nachteil: Klassen von sun.* unterliegen in späteren Versionen ändern (allthough diese Methode seit Java 5 mit der gleichen Signatur vorhanden ist) und sind für alle Java-Implementierungen nicht verfügbar, siehe this discussion.

Wenn das ein Problem ist: Sie könnten einen generischen Proxy mit Ihrer eigenen InvocationHandler erstellen - das ist genau das, was AnnotationParser intern für Sie tut. Oder Sie verwenden Ihre eigene Implementierung MyAnnotation wie definiert here. In beiden Fällen sollten Sie daran denken, annotationType(), equals() und hashCode() zu implementieren, da das Ergebnis speziell für java.lang.Annotation dokumentiert ist.

Map<String, Object> annotationParameters = new HashMap<>(); 
annotationParameters.put("name", "someName"); 
MyAnnotation myAnnotation = TypeFactory.annotation(MyAnnotation.class, annotationParameters); 

Diese auf eine Anmerkung Äquivalent produzieren, was Sie von bekäme:

7

Der Proxy-Ansatz, wie er in Gunnar's answer vorgeschlagen wird bereits in GeAnTyRef implementiert

@MyAnnotation(name = "someName") 

Annotation Instanzen dies erzeugt Way wird identisch mit den von Java hergestellten funktionieren, und ihre hashCode und equals wurden aus Kompatibilitätsgründen korrekt implementiert, also keine bizarren Vorbehalte wie bei direc Die Annotation wird wie in der akzeptierten Antwort instanziiert. Tatsächlich verwendet JDK intern denselben Ansatz: sun.reflect.annotation.AnnotationParser#annotationForMap.

Die Bibliothek selbst ist winzig und hat keine Abhängigkeiten.

Offenlegung: Ich bin der Entwickler hinter GeAnTyRef.

+1

Ich würde dies gegenüber den anderen Ansätzen bevorzugen, da es das beste Ergebnis liefert (dasselbe wie [Gunnars Antwort] (https://Stackoverflow.com/a/16326389/294657)) und weil es keine internen JDK-Klassen von Oracle verwendet wie Tobias 'Antwort, die es wahrscheinlich mit anderen JDKs/JVMs inkompatibel macht. – hoijui

Verwandte Themen