É possível ler o valor de uma anotação em java?

99

este é o meu 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;
 }

é possível ler o valor da minha anotação @Column ( columnName = "xyz123") em outra classe?

BeeS
fonte

Respostas:

122

Sim, se sua anotação de coluna tem retenção de tempo de execução

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

você pode fazer algo assim

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

ATUALIZAÇÃO: Para obter campos privados, use

Myclass.class.getDeclaredFields()
Cefalópode
fonte
1
eu gosto da sua solução. Como podemos torná-lo mais genérico, como em vez de MyClass, eu quero usar T como para (Campo f: T.class.getFields ()) {Column column = f.getAnnotation (Column.class); if (coluna! = nulo) System.out.println (column.columnName ()); }
ATHER
1
Exatamente! Eu tenho lutado para descobrir isso também. E se eu quiser ter um processador de anotações que não precise ser explicitamente fornecido com um nome de classe? Pode ser feito para pegá-lo do contexto; 'isto'??
5122014009 de
Não tenho certeza se entendi o que vocês dois precisam. Faça isso como uma nova pergunta com um exemplo completo. Você pode vinculá-lo aqui se desejar.
Cefalópode de
3
Use Myclass.class.getDeclaredFields()para obter campos privados
q0re
Funcionou para mim obrigado. Eu estava procurando por campos privados da superclasse, então usei clsName.getSuperclass (). GetDeclaredFields ()
Shashank
88

Claro que é. Aqui está um exemplo de anotação:

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

    String testText();
}

E um exemplo de método anotado:

class TestClass {

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

E um método de amostra em outra classe que imprime o valor do 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());
    }
}

Não é muito diferente para anotações de campo como as suas.

Cheerz!

Lachezar Balev
fonte
21

Nunca fiz isso, mas parece que o Reflection fornece isso. Fieldé um AnnotatedElemente assim foi getAnnotation. Esta página tem um exemplo (copiado abaixo); bastante simples se você conhece a classe da anotação e se a política de anotação retém a anotação em tempo de execução. Naturalmente, se a política de retenção não mantiver a anotação em tempo de execução, você não poderá consultá-la em tempo de execução.

Uma resposta que já foi excluída (?) Forneceu um link útil para um tutorial de anotações que pode ser útil para você; Copiei o link aqui para que as pessoas possam usá-lo.

Exemplo desta 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
fonte
6

Elaborando a resposta de @Cefalópode, se você quisesse todos os nomes de coluna em uma lista, você poderia usar esta linha:

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
fonte
Objects.nonNull para abraçar totalmente o Java 8 :) .filter (f -> nonNull (f.getAnnotation (Column.class)))
desumanizador
4

Embora todas as respostas fornecidas até agora sejam perfeitamente válidas, deve-se também ter em mente a biblioteca de reflexões do Google para uma abordagem mais genérica e fácil de digitalização de anotações, por exemplo

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

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

Você também pode usar tipos genéricos, no meu caso, levando em consideração tudo o que foi dito antes, você pode fazer 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();

    }

}

Lembre-se de que isso não equivalerá em 100% a SomeClass.class.get (...) ();

Mas pode fazer o truque ...

SigmaSoldier
fonte
3

Em caso comum, você tem acesso privado para os campos, então você NÃO PODE usar getFields na reflexão. Em vez disso, você deve usar getDeclaredFields

Portanto, em primeiro lugar, você deve estar ciente se sua anotação de coluna tem a retenção de tempo de execução:

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

Depois disso, você pode fazer algo assim:

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

Obviamente, você gostaria de fazer algo com o campo - definir um novo valor usando o valor da anotação:

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

Portanto, o código completo pode ser semelhante a este:

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

Para as poucas pessoas que pedem um método genérico, isso deve ajudá-lo (5 anos depois: p).

Para o meu exemplo abaixo, estou puxando o valor de URL RequestMapping de métodos que têm a anotação RequestMapping. Para adaptar isso aos campos, basta alterar o

for (Method method: clazz.getMethods())

para

for (Field field: clazz.getFields())

E troque o uso de RequestMapping por qualquer anotação que você queira ler. Mas certifique-se de que a anotação tenha @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
fonte
0

uma das maneiras que usei:

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
fonte