¿Es posible leer el valor de una anotación en java?

98

este es mi código:

@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;
 }

¿Es posible leer el valor de mi anotación @Column ( columnName = "xyz123") en otra clase?

Abejas
fuente

Respuestas:

122

Sí, si su anotación de columna tiene la retención de tiempo de ejecución

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

puedes hacer algo como esto

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

ACTUALIZACIÓN: Para obtener campos privados, use

Myclass.class.getDeclaredFields()
Cefalópodo
fuente
1
me gusta tu solución. ¿Cómo podemos hacerlo más genérico como en lugar de MyClass quiero usar T como para (Field f: T.class.getFields ()) {Column column = f.getAnnotation (Column.class); if (column! = null) System.out.println (column.columnName ()); }
ATHER
1
¡Exactamente! Yo también he estado luchando por darme cuenta de eso. ¿Qué sucede si quiero tener un procesador de anotaciones que no necesita que se le proporcione explícitamente un nombre de clase? ¿Puede hacerse que lo recoja del contexto? 'esta'??
5122014009
No estoy seguro de entender qué necesitan ustedes dos. Hágala como una nueva pregunta con un ejemplo completo. Puede vincularlo aquí si lo desea.
Cefalópodo
3
Úselo Myclass.class.getDeclaredFields()para obtener campos privados
q0re
Funcionó para mí. Gracias. Estaba buscando campos privados de superclase, así que usé clsName.getSuperclass (). GetDeclaredFields ()
Shashank
88

Por supuesto que es. Aquí hay una anotación de muestra:

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

    String testText();
}

Y un método anotado de muestra:

class TestClass {

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

Y un método de muestra en otra clase que imprime el valor del 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());
    }
}

No es muy diferente para anotaciones de campo como la suya.

¡Cheerz!

Lachezar Balev
fuente
21

Nunca lo he hecho, pero parece que Reflection proporciona esto. Fieldes un AnnotatedElementy así ha sido getAnnotation. Esta página tiene un ejemplo (copiado a continuación); bastante sencillo si conoce la clase de la anotación y si la política de anotaciones retiene la anotación en tiempo de ejecución. Naturalmente, si la política de retención no mantiene la anotación en tiempo de ejecución, no podrá consultarla en tiempo de ejecución.

Una respuesta que se ha eliminado desde entonces (?) Proporcionó un enlace útil a un tutorial de anotaciones que puede resultarle útil; He copiado el enlace aquí para que la gente pueda usarlo.

Ejemplo de esta página :

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
fuente
6

Desarrollando la respuesta de @Cephalopod, si quisiera todos los nombres de columna en una lista, puede usar este delineador:

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
fuente
Objects.nonNull para adoptar por completo Java 8 :) .filter (f -> nonNull (f.getAnnotation (Column.class)))
deshumanizador
4

Si bien todas las respuestas dadas hasta ahora son perfectamente válidas, también se debe tener en cuenta la biblioteca de reflexiones de Google para un enfoque más genérico y fácil del escaneo de anotaciones, p.

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

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

También puedes usar tipos genéricos, en mi caso, teniendo en cuenta todo lo dicho antes puedes hacer algo como:

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

    }

}

Recuerde que esto no equivaldrá al 100% a SomeClass.class.get (...) ();

Pero puede hacer el truco ...

SigmaSoldado
fuente
3

En el caso común, tiene acceso privado a los campos, por lo que NO PUEDE usar getFields como reflejo. En lugar de esto, debe usar getDeclaredFields

Entonces, en primer lugar, debe tener en cuenta si su anotación de columna tiene la retención de tiempo de ejecución:

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

Después de eso, puedes hacer algo como esto:

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

Obviamente, le gustaría hacer algo con el campo: establezca un nuevo valor usando el valor de anotación:

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

Entonces, el código completo se puede ver así:

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
fuente
2

Para las pocas personas que solicitan un método genérico, esto debería ayudarlo (5 años después: p).

Para mi siguiente ejemplo, estoy extrayendo el valor de URL RequestMapping de métodos que tienen la anotación RequestMapping. Para adaptar esto a los campos, simplemente cambie el

for (Method method: clazz.getMethods())

a

for (Field field: clazz.getFields())

Y cambie el uso de RequestMapping por cualquier anotación que desee leer. Pero asegúrese de que la anotación tenga @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 "";
}
MattWeiler
fuente
0

una de las formas en que lo usé:

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
fuente