web-dev-qa-db-fra.com

Est-il possible de lire la valeur d'une annotation en Java?

c'est mon 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;
 }

est-il possible de lire la valeur de mon annotation @Column ( columnName = "xyz123") dans une autre classe?

85
BeeS

Oui, si votre annotation de colonne a la rétention d'exécution

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

tu peux faire quelque chose comme ça

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

UPDATE: Pour obtenir des champs privés, utilisez

Myclass.class.getDeclaredFields()
99
Cephalopod

Bien sûr que ça l'est. Voici un exemple d'annotation:

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

    String testText();
}

Et un exemple de méthode annotée:

class TestClass {

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

Et un exemple de méthode dans une autre classe qui affiche la valeur du testText:

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());
    }
}

Pas très différent pour les annotations sur le terrain comme le vôtre.

Cheerz!

75
Lachezar Balev

Je ne l'ai jamais fait, mais il semble que Reflection fournit ceci. Field est un AnnotatedElement et il en a getAnnotation . Cette page a un exemple (copié ci-dessous); assez simple si vous connaissez la classe de l'annotation et si la règle d'annotation la conserve au moment de l'exécution. Naturellement, si la stratégie de rétention ne conserve pas l'annotation au moment de l'exécution, vous ne pourrez pas l'interroger au moment de l'exécution.

Une réponse qui a été supprimée depuis (?) A fourni un lien utile vers un didacticiel sur les annotations que vous pouvez trouver utile; J'ai copié le lien ici pour que les gens puissent l'utiliser.

Exemple de cette page :

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);
  }
}
19
T.J. Crowder

En développant la réponse de @Cephalopod, si vous voulez tous les noms de colonnes dans une liste, vous pouvez utiliser cette option:

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());
4
Simon Baars

Bien que toutes les réponses données jusqu’à présent soient parfaitement valables, il convient également de garder à l’esprit la bibliothèque de réflexions de Google pour une approche plus générique et plus simple du balayage des annotations, par exemple.

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

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

Vous pouvez également utiliser des types génériques, dans mon cas, en prenant en compte tout ce qui a été dit avant de pouvoir faire quelque chose comme:

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();

    }

}

Rappelez-vous que cela ne sera pas équivalent à 100% à SomeClass.class.get (...) ();

Mais peut faire le tour ...

3
SigmaSoldier

Dans la plupart des cas, vous avez private accès aux champs, vous ne pouvez donc PAS utiliser getFields en réflexion. Au lieu de cela, vous devriez utiliser getDeclaredFields

Donc, tout d’abord, vous devez savoir si votre annotation de colonne a la rétention d’exécution:

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

Après cela, vous pouvez faire quelque chose comme ça:

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

Évidemment, vous voudriez faire quelque chose avec field - définir une nouvelle valeur en utilisant la valeur d'annotation:

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

Ainsi, le code complet peut ressembler à ceci:

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()
                )
        );
}
3
Oleg Poltoratskii

Pour les quelques personnes qui demandent une méthode générique, cela devrait vous aider (5 ans plus tard: p).

Pour mon exemple ci-dessous, je tire la valeur de l'URL RequestMapping à partir de méthodes portant l'annotation RequestMapping . Pour l'adapter aux champs, modifiez simplement le

for (Method method: clazz.getMethods())

à

for (Field field: clazz.getFields())

Et permutez l'utilisation de RequestMapping pour n'importe quelle annotation que vous cherchez à lire . Mais assurez-vous que l'annotation a @Retention (RetentionPolicy.RUNTIME) .

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 "";
}
0
MattWeiler