Czy można odczytać wartość adnotacji w Javie?

99

to jest mój kod:

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

Jest to możliwe, aby odczytać wartość mojego adnotacji @Column ( columnName = „xyz123”) w innej klasy?

Pszczoły
źródło

Odpowiedzi:

122

Tak, jeśli adnotacja kolumny ma czas przechowywania w czasie wykonywania

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

możesz zrobić coś takiego

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

AKTUALIZACJA: Aby uzyskać prywatne pola, użyj

Myclass.class.getDeclaredFields()
Głowonóg
źródło
1
Podoba mi się twoje rozwiązanie. Jak możemy uczynić go bardziej ogólnym, jak zamiast MyClass, chcę użyć T like for (Field f: T.class.getFields ()) {Column column = f.getAnnotation (Column.class); if (kolumna! = null) System.out.println (kolumna.nazwaKolumny ()); }
ATHER
1
Dokładnie! Ja też starałem się to rozgryźć. A jeśli chcę mieć procesor adnotacji, któremu nie trzeba jawnie podawać nazwy klasy? Czy można go wydobyć z kontekstu; 'to'??
5122014009
Nie jestem pewien, czy rozumiem, czego potrzebujecie we dwoje. Proszę zadać to jako nowe pytanie z pełnym przykładem. Jeśli chcesz, możesz je tutaj połączyć.
Głowonóg,
3
Użyj, Myclass.class.getDeclaredFields()aby uzyskać prywatne pola
q0re
U mnie to zadziałało. dzięki. Szukałem prywatnych pól superclass, więc użyłem clsName.getSuperclass (). GetDeclaredFields ()
Shashank
88

Oczywiście, że jest. Oto przykładowa adnotacja:

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

    String testText();
}

I przykładowa metoda z adnotacjami:

class TestClass {

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

I przykładowa metoda w innej klasie, która drukuje wartość 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());
    }
}

Niewiele różni się od adnotacji pól, takich jak Twoja.

Cheerz!

Lachezar Balev
źródło
21

Nigdy tego nie robiłem, ale wygląda na to, że Reflection to zapewnia. Fieldjest AnnotatedElementi tak jest getAnnotation. Ta strona zawiera przykład (skopiowany poniżej); dość proste, jeśli znasz klasę adnotacji i jeśli zasada adnotacji zachowuje adnotację w czasie wykonywania. Oczywiście, jeśli zasady przechowywania nie zachowują adnotacji w czasie wykonywania, nie będzie można wysyłać do niej zapytań w czasie wykonywania.

Odpowiedź, która została usunięta (?), Stanowiła przydatne łącze do samouczka dotyczącego adnotacji , który może okazać się pomocny; Skopiowałem link tutaj, aby ludzie mogli go używać.

Przykład z tej strony :

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
źródło
6

Rozwijając odpowiedź @Cephalopod, jeśli chcesz, aby wszystkie nazwy kolumn znajdowały się na liście, możesz użyć tego onelinera:

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
źródło
Objects.nonNull, aby w pełni objąć Javę 8 :) .filter (f -> nonNull (f.getAnnotation (Column.class)))
dehumanizer
4

Chociaż wszystkie dotychczas udzielone odpowiedzi są całkowicie prawidłowe, należy również pamiętać o bibliotece refleksji Google, aby uzyskać bardziej ogólne i łatwiejsze podejście do skanowania adnotacji, np.

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

 Set<Field> ids = reflections.getFieldsAnnotatedWith(javax.persistence.Id.class);
Fritz Duchardt
źródło
3

Możesz także użyć typów ogólnych, w moim przypadku, biorąc pod uwagę wszystko, co zostało powiedziane, zanim będziesz mógł zrobić coś takiego:

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

    }

}

Pamiętaj, że to nie będzie równoważne w 100% SomeClass.class.get (...) ();

Ale może załatwić sprawę ...

SigmaSoldier
źródło
3

W typowym przypadku masz prywatny dostęp do pól, więc NIE MOŻESZ używać getFields w refleksji. Zamiast tego powinieneś użyć getDeclaredFields

Po pierwsze, powinieneś wiedzieć, czy adnotacja w kolumnie ma czas przechowywania w czasie wykonywania:

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

Następnie możesz zrobić coś takiego:

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

Oczywiście chciałbyś coś zrobić z polem - ustaw nową wartość używając wartości adnotacji:

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

Tak więc pełny kod może wyglądać następująco:

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
źródło
2

Dla nielicznych osób proszących o ogólną metodę powinno ci to pomóc (5 lat później: p).

W poniższym przykładzie pobieram wartość adresu URL RequestMapping z metod, które mają adnotację RequestMapping. Aby dostosować to do pól, po prostu zmień

for (Method method: clazz.getMethods())

do

for (Field field: clazz.getFields())

I zamień użycie RequestMapping na dowolną adnotację, którą chcesz przeczytać. Ale upewnij się, że adnotacja ma @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
źródło
0

jeden ze sposobów, w jaki go użyłem:

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
źródło