Ist es möglich, den Wert einer Anmerkung in Java zu lesen?

98

Das ist mein Code:

@Column(columnName="firstname")


private String firstName;

 @Column(columnName="lastname")
 private String lastName;

 public String getFirstName() {
  return firstName;
 }

 public void setFirstName(String firstName) {
  this.firstName = firstName;
 }

 public String getLastName() {
  return lastName;
 }

 public void setLastName(String lastName) {
  this.lastName = lastName;
 }

Ist es möglich, den Wert meiner Anmerkung @Column ( columnName = "xyz123") in einer anderen Klasse zu lesen ?

Bienen
quelle

Antworten:

122

Ja, wenn Ihre Spaltenanmerkung die Laufzeitaufbewahrung aufweist

@Retention(RetentionPolicy.RUNTIME)
@interface Column {
    ....
}

Sie können so etwas tun

for (Field f: MyClass.class.getFields()) {
   Column column = f.getAnnotation(Column.class);
   if (column != null)
       System.out.println(column.columnName());
}

UPDATE: Um private Felder abzurufen, verwenden Sie

Myclass.class.getDeclaredFields()
Kopffüßer
quelle
1
Ich mag deine Lösung. Wie können wir es generischer machen als anstelle von MyClass? Ich möchte T wie für verwenden (Feld f: T.class.getFields ()) {Column column = f.getAnnotation (Column.class); if (column! = null) System.out.println (column.columnName ()); }
ATHER
1
Genau! Ich habe mich bemüht, das auch herauszufinden. Was ist, wenn ich einen Anmerkungsprozessor haben möchte, der nicht explizit mit einem Klassennamen versehen werden muss? Kann es dazu gebracht werden, es aus dem Kontext aufzunehmen? 'Dies'??
5122014009
Ich bin mir nicht sicher, ob ich verstehe, was Sie beide brauchen. Bitte stellen Sie dies als neue Frage mit einem vollständigen Beispiel. Sie können es hier verlinken, wenn Sie möchten.
Kopffüßer
3
Verwenden Sie Myclass.class.getDeclaredFields(), um private Felder zu erhalten
q0re
Es hat bei mir funktioniert. Vielen Dank. Ich suchte nach privaten Feldern der Superklasse, also benutzte ich clsName.getSuperclass (). GetDeclaredFields ()
Shashank
88

Natürlich ist es das. Hier ist eine Beispielanmerkung:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {

    String testText();
}

Und eine kommentierte Beispielmethode:

class TestClass {

    @TestAnnotation(testText="zyx")
    public void doSomething() {}
}

Und eine Beispielmethode in einer anderen Klasse, die den Wert des testText druckt:

Method[] methods = TestClass.class.getMethods();
for (Method m : methods) {
    if (m.isAnnotationPresent(TestAnnotation.class)) {
        TestAnnotation ta = m.getAnnotation(TestAnnotation.class);
        System.out.println(ta.testText());
    }
}

Nicht viel anders für Feldanmerkungen wie Ihre.

Cheerz!

Lachezar Balev
quelle
21

Ich habe es noch nie gemacht, aber es sieht so aus, als ob Reflection dies bietet. Fieldist ein AnnotatedElementund so hat es getAnnotation. Diese Seite enthält ein Beispiel (unten kopiert). Ganz einfach, wenn Sie die Klasse der Annotation kennen und wenn die Annotationsrichtlinie die Annotation zur Laufzeit beibehält. Wenn die Aufbewahrungsrichtlinie die Anmerkung zur Laufzeit nicht beibehält, können Sie sie zur Laufzeit natürlich nicht abfragen.

Eine Antwort, die inzwischen gelöscht wurde (?), Bietet einen nützlichen Link zu einem Tutorial zu Anmerkungen , das Sie möglicherweise hilfreich finden. Ich habe den Link hier kopiert, damit die Leute ihn verwenden können.

Beispiel von dieser Seite :

import java.lang.annotation.Retention; 
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;

@Retention(RetentionPolicy.RUNTIME)
@interface MyAnno {
  String str();

  int val();
}

class Meta {
  @MyAnno(str = "Two Parameters", val = 19)
  public static void myMeth(String str, int i) {
    Meta ob = new Meta();

    try {
      Class c = ob.getClass();

      Method m = c.getMethod("myMeth", String.class, int.class);

      MyAnno anno = m.getAnnotation(MyAnno.class);

      System.out.println(anno.str() + " " + anno.val());
    } catch (NoSuchMethodException exc) {
      System.out.println("Method Not Found.");
    }
  }

  public static void main(String args[]) {
    myMeth("test", 10);
  }
}
TJ Crowder
quelle
6

Wenn Sie auf die Antwort von @Cephalopod eingehen und alle Spaltennamen in einer Liste haben möchten, können Sie diesen Oneliner verwenden:

List<String> columns = 
        Arrays.asList(MyClass.class.getFields())
              .stream()
              .filter(f -> f.getAnnotation(Column.class)!=null)
              .map(f -> f.getAnnotation(Column.class).columnName())
              .collect(Collectors.toList());
Simon Baars
quelle
Objects.nonNull, um Java 8 vollständig zu nutzen :) .filter (f -> nonNull (f.getAnnotation (Column.class)))
Entmenschlichung
4

Während alle bisher gegebenen Antworten vollkommen gültig sind, sollte man auch die Google Reflections-Bibliothek berücksichtigen, um einen allgemeineren und einfacheren Ansatz für das Scannen von Anmerkungen zu erhalten, z

 Reflections reflections = new Reflections("my.project.prefix");

 Set<Field> ids = reflections.getFieldsAnnotatedWith(javax.persistence.Id.class);
Fritz Duchardt
quelle
3

Sie können in meinem Fall auch generische Typen verwenden, wobei Sie alles berücksichtigen, was gesagt wurde, bevor Sie etwas tun können wie:

public class SomeTypeManager<T> {

    public SomeTypeManager(T someGeneric) {

        //That's how you can achieve all previously said, with generic types.
        Annotation[] an = someGeneric.getClass().getAnnotations();

    }

}

Denken Sie daran, dass dies nicht zu 100% mit SomeClass.class.get (...) () übereinstimmt.

Aber kann den Trick machen ...

SigmaSoldier
quelle
3

In der Regel haben Sie privaten Zugriff auf Felder, sodass Sie getFields NICHT in Reflection verwenden können. Stattdessen sollten Sie getDeclaredFields verwenden

Zunächst sollten Sie sich also darüber im Klaren sein, ob Ihre Spaltenanmerkung die Laufzeitaufbewahrung aufweist:

@Retention(RetentionPolicy.RUNTIME)
@interface Column {
}

Danach können Sie so etwas tun:

for (Field f: MyClass.class.getDeclaredFields()) {
   Column column = f.getAnnotation(Column.class);
       // ...
}

Natürlich möchten Sie etwas mit dem Feld tun - setzen Sie einen neuen Wert mit dem Anmerkungswert:

Column annotation = f.getAnnotation(Column.class);
if (annotation != null) {
    new PropertyDescriptor(f.getName(), Column.class).getWriteMethod().invoke(
        object,
        myCoolProcessing(
            annotation.value()
        )
    );
}

Der vollständige Code kann also folgendermaßen aussehen:

for (Field f : MyClass.class.getDeclaredFields()) {
    Column annotation = f.getAnnotation(Column.class);
    if (annotation != null)
        new PropertyDescriptor(f.getName(), Column.class).getWriteMethod().invoke(
                object,
                myCoolProcessing(
                        annotation.value()
                )
        );
}
Oleg Poltoratskii
quelle
2

Für die wenigen Menschen, die nach einer generischen Methode fragen, sollte dies hilfreich sein (5 Jahre später: p).

In meinem folgenden Beispiel ziehe ich den RequestMapping-URL-Wert aus Methoden mit der RequestMapping-Annotation. Um dies für Felder anzupassen, ändern Sie einfach die

for (Method method: clazz.getMethods())

zu

for (Field field: clazz.getFields())

Tauschen Sie die Verwendung von RequestMapping gegen die Anmerkungen aus, die Sie lesen möchten . Stellen Sie jedoch sicher, dass die Annotation @Retention (RetentionPolicy.RUNTIME) enthält .

public static String getRequestMappingUrl(final Class<?> clazz, final String methodName)
{
    // Only continue if the method name is not empty.
    if ((methodName != null) && (methodName.trim().length() > 0))
    {
        RequestMapping tmpRequestMapping;
        String[] tmpValues;

        // Loop over all methods in the class.
        for (Method method: clazz.getMethods())
        {
            // If the current method name matches the expected method name, then keep going.
            if (methodName.equalsIgnoreCase(method.getName()))
            {
                // Try to extract the RequestMapping annotation from the current method.
                tmpRequestMapping = method.getAnnotation(RequestMapping.class);

                // Only continue if the current method has the RequestMapping annotation.
                if (tmpRequestMapping != null)
                {
                    // Extract the values from the RequestMapping annotation.
                    tmpValues = tmpRequestMapping.value();

                    // Only continue if there are values.
                    if ((tmpValues != null) && (tmpValues.length > 0))
                    {
                        // Return the 1st value.
                        return tmpValues[0];
                    }
                }
            }
        }
    }

    // Since no value was returned, log it and return an empty string.
    logger.error("Failed to find RequestMapping annotation value for method: " + methodName);

    return "";
}
MattWeiler
quelle
0

eine der Möglichkeiten, wie ich es benutzt habe:

protected List<Field> getFieldsWithJsonView(Class sourceClass, Class jsonViewName){
    List<Field> fields = new ArrayList<>();
    for (Field field : sourceClass.getDeclaredFields()) {
        JsonView jsonViewAnnotation = field.getDeclaredAnnotation(JsonView.class);
        if(jsonViewAnnotation!=null){
            boolean jsonViewPresent = false;
            Class[] viewNames = jsonViewAnnotation.value();
            if(jsonViewName!=null && Arrays.asList(viewNames).contains(jsonViewName) ){
                fields.add(field);
            }
        }
    }
    return fields;
}    
RK Punjal
quelle