2011-12-15 9 views

Odpowiedz

128

Nie używać wyrażenia regularnego, użyj Introspector:

for(PropertyDescriptor propertyDescriptor : 
    Introspector.getBeanInfo(yourClass).getPropertyDescriptors()){ 

    // propertyEditor.getReadMethod() exposes the getter 
    // btw, this may be null if you have a write-only property 
    System.out.println(propertyDescriptor.getReadMethod()); 
} 

Zwykle nie chcesz właściwości z Object.class, więc chcesz użyć metody z dwoma parametrami:

Introspector.getBeanInfo(yourClass, stopClass) 
// usually with Object.class as 2nd param 
// the first class is inclusive, the second exclusive 

BTW: istnieją ramy, które robią to za Ciebie i prezentują Ci widok wysokiego poziomu. Na przykład. commons/beanutils ma metodę

Map<String, String> properties = BeanUtils.describe(yourObject); 

(docs here), która właśnie to robi: odnaleźć i wykonać wszystkie pobierające i zapisać wynik w mapie. Niestety, BeanUtils.describe() konwertuje wszystkie wartości właściwości na ciągi przed powrotem. WTF. Dzięki @danw


Aktualizacja:

Oto Java 8 metoda, która zwraca Map<String, Object> na podstawie właściwości fasoli obiektu.

public static Map<String, Object> beanProperties(Object bean) { 
    try { 
    return Arrays.asList(
     Introspector.getBeanInfo(bean.getClass(), Object.class) 
        .getPropertyDescriptors() 
    ) 
     .stream() 
     // filter out properties with setters only 
     .filter(pd -> Objects.nonNull(pd.getReadMethod())) 
     .collect(Collectors.toMap(
     // bean property name 
     PropertyDescriptor::getName, 
     pd -> { // invoke method to get value 
      try { 
       return pd.getReadMethod().invoke(bean); 
      } catch (Exception e) { 
       // replace this with better error handling 
       return null; 
      } 
     })); 
    } catch (IntrospectionException e) { 
    // and this, too 
    return Collections.emptyMap(); 
    } 
} 

Prawdopodobnie jednak chcesz poprawić obsługę błędów. Przykro nam z powodu obowiązującej normy, sprawdzone wyjątki uniemożliwiają nam pełną funkcjonalność.


Okazało się, że Collectors.toMap() nienawidzi wartości null. Oto bardziej niezbędna wersja powyższego kodu:

public static Map<String, Object> beanProperties(Object bean) { 
    try { 
     Map<String, Object> map = new HashMap<>(); 
     Arrays.asList(Introspector.getBeanInfo(bean.getClass(), Object.class) 
            .getPropertyDescriptors()) 
       .stream() 
       // filter out properties with setters only 
       .filter(pd -> Objects.nonNull(pd.getReadMethod())) 
       .forEach(pd -> { // invoke method to get value 
        try { 
         Object value = pd.getReadMethod().invoke(bean); 
         if (value != null) { 
          map.put(pd.getName(), value); 
         } 
        } catch (Exception e) { 
         // add proper error handling here 
        } 
       }); 
     return map; 
    } catch (IntrospectionException e) { 
     // and here, too 
     return Collections.emptyMap(); 
    } 
} 

oto ta sama funkcjonalność w bardziej zwięzły sposób, używając JavaSlang:

public static Map<String, Object> javaSlangBeanProperties(Object bean) { 
    try { 
     return Stream.of(Introspector.getBeanInfo(bean.getClass(), Object.class) 
            .getPropertyDescriptors()) 
        .filter(pd -> pd.getReadMethod() != null) 
        .toJavaMap(pd -> { 
         try { 
          return new Tuple2<>(
            pd.getName(), 
            pd.getReadMethod().invoke(bean)); 
         } catch (Exception e) { 
          throw new IllegalStateException(); 
         } 
        }); 
    } catch (IntrospectionException e) { 
     throw new IllegalStateException(); 

    } 
} 

A oto wersja Guava:

public static Map<String, Object> guavaBeanProperties(Object bean) { 
    Object NULL = new Object(); 
    try { 
     return Maps.transformValues(
       Arrays.stream(
         Introspector.getBeanInfo(bean.getClass(), Object.class) 
            .getPropertyDescriptors()) 
         .filter(pd -> Objects.nonNull(pd.getReadMethod())) 
         .collect(ImmutableMap::<String, Object>builder, 
           (builder, pd) -> { 
            try { 
             Object result = pd.getReadMethod() 
                 .invoke(bean); 
             builder.put(pd.getName(), 
                firstNonNull(result, NULL)); 
            } catch (Exception e) { 
             throw propagate(e); 
            } 
           }, 
           (left, right) -> left.putAll(right.build())) 
         .build(), v -> v == NULL ? null : v); 
    } catch (IntrospectionException e) { 
     throw propagate(e); 
    } 
} 
+5

Wow. Nie wiedziałem, że możesz to zrobić! Chłodny! –

+0

Dzięki ..i przetestuj kod ... koniec wyniku to ** publiczny końcowy natywny java.lang.Class java.lang.Object.getClass() ** ... nie chcę go wywoływać. . Jak go usunąć? – user996505

+1

@ user996505 Użyj Introspector.getBeanInfo (yourClass, Object.class), aby wyszukać wszystkie klasy poniżej Obiekt –

9
// Get the Class object associated with this class. 
    MyClass myClass= new MyClass(); 
    Class objClass= myClass.getClass(); 

    // Get the public methods associated with this class. 
    Method[] methods = objClass.getMethods(); 
    for (Method method:methods) 
    { 
     System.out.println("Public method found: " + method.toString()); 
    } 
+1

Tak, ale będziesz musiał również sprawdzić dla każdej metody, czy jest ona publiczna, niestatyczna, zwraca nieważne, nie oczekuje żadnego parametru i przestrzega konwencji get/isXyz. Introspector robi to za Ciebie, a także buforuje dane BeanInfo wewnętrznie dla innych aplikacji. –

+0

nie zwraca pustki, czyli –

16

Można użyć ramy Reflections dla tego

import org.reflections.ReflectionUtils.*; 
Set<Method> getters = ReflectionUtils.getAllMethods(someClass, 
     ReflectionUtils.withModifier(Modifier.PUBLIC), ReflectionUtils.withPrefix("get")); 
+0

Nie wszystkie pobierające rozpoczynają się od "get": (1) gettery zwracające wartość boolean mogą zaczynać się od "is"; (2) klasa BeanInfo może określać, że dodatkowe metody to gettery.Naprawdę powinieneś dodać ograniczenie takie jak _ jeśli wiesz, że wszystkie twoje działania zaczynają się od "get", możesz to zrobić_. – toolforger

-4

Należy zachować ogólny getter w każdym fasoli, tak aby wywołać getAttribute1() powinna być w stanie wywołać rodzajowe getter get („atrybut1”)

Ta ogólna getter wywoła w zakręcie prawidłowa getter

Object get(String attribute) 
{ 
    if("Attribute1".equals(attribute) 
    { 
     return getAttribute1(); 
    } 
} 

Takie podejście wiąże się do utrzymania tej oddzielną listę w każdym bean, ale ten sposób uniknąć refleksji, która ma problemy z wydajnością, więc jeśli piszesz kod produkcyjny, który musi mieć dobre wyniki można wykorzystać powyższe wzór dla wszystkich twoich ziaren.

Jeśli jest to jakiś kod testowy lub kod użytkowy, który nie ma wysokich wymagań dotyczących wydajności, lepiej jest przyjąć inne podejście, ponieważ takie podejście jest podatne na błędy, chyba że można napisać pewien rodzaj kontrolera czasu kompilacji, który zapewnia tę ogólną funkcję pobierającą działa dla wszystkich atrybutów.

7

Spring oferuje łatwy BeanUtil method dla Bean introspekcji:

PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(clazz, property); 
Method getter = pd.getReadMethod(); 
0

Ten kod jest testowany OK.

private void callAllGetterMethodsInTestModel(TestModel testModelObject) { 
     try { 
      Class testModelClass = Class.forName("com.encoders.eva.testreflectionapi.TestModel"); 
      Method[] methods = testModelClass.getDeclaredMethods(); 
      ArrayList<String> getterResults = new ArrayList<>(); 
      for (Method method : 
        methods) { 
       if (method.getName().startsWith("get")){ 
        getterResults.add((String) method.invoke(testModelObject)); 
       } 
      } 
      Log.d("sayanReflextion", "==>: "+getterResults.toString()); 
     } catch (ClassNotFoundException | IllegalAccessException | InvocationTargetException e) { 
      e.printStackTrace(); 
     } 
    } 
Powiązane problemy