2017-05-26 151 views
0

我經常需要處理包含其他DTO的DTO,並且我想掃描一個對象的屬性(以及它們自己的屬性,遞歸地),並檢索類Bingo中的所有可訪問對象整個等級。在嵌套對象中查找特定類型的屬性

例如,當我有以下幾點:

public static class Bingo { 
    // the one I want to get 
} 

public static class Foo { 
    private Bar bar; 
    private Bingo bingo; 
    private List<Bingo> bingos; 

    // getters & setters 
} 

public static class Bar { 

    private Bingo bingo; 

    // getters & setters 
} 

我希望得到我的Foo對象的屬性,包括Bar對象的那些和List發現Bingo所有實例。

有沒有一個庫方便地做到這一點?

的更完整的測試案例(使用位的JUnit):

public static class Bingo { 
    private final int id; 

    public Bingo(int in_id) { 
     id = in_id; 
    } 

    @Override 
    public String toString() { 
     return "Bingo#"+String.valueOf(id); 
    } 

} 

public static class BingoWrapper { 

    private Bingo bingo; 

    public Bingo getBingo() { 
     return bingo; 
    } 

    public void setBingo(Bingo in_bingo) { 
     bingo = in_bingo; 
    } 
} 

public static class BingoFactory { 

    private final List<Bingo> ALL_BINGOS = new ArrayList<>(); 
    private int sequence = 0; 

    public Bingo createBingo(){ 
     Bingo l_bingo = new Bingo(sequence++); 
     ALL_BINGOS.add(l_bingo); 
     return l_bingo; 
    } 

    public BingoWrapper createBingoWrapper(){ 
     BingoWrapper l_bar = new BingoWrapper(); 
     l_bar.setBingo(createBingo()); 
     return l_bar; 
    } 

    public List<Bingo> getAllBingos(){ 
     return ALL_BINGOS.stream().collect(Collectors.toList()); 
    } 

} 

public static class Foo { 

    private Bingo bingo; 
    private BingoWrapper wrapper; 
    private Bingo[] array; 
    private Collection<Object> collection; 
    private Map<Object,Object> map; 

    public Bingo getBingo() { 
     return bingo; 
    } 
    public void setBingo(Bingo in_bingo) { 
     bingo = in_bingo; 
    } 
    public BingoWrapper getWrapper() { 
     return wrapper; 
    } 
    public void setWrapper(BingoWrapper in_bar) { 
     wrapper = in_bar; 
    } 
    public Bingo[] getArray() { 
     return array; 
    } 
    public void setArray(Bingo[] in_array) { 
     array = in_array; 
    } 
    public Collection<Object> getCollection() { 
     return collection; 
    } 
    public void setCollection(Collection<Object> in_collection) { 
     collection = in_collection; 
    } 
    public Map<Object, Object> getMap() { 
     return map; 
    } 
    public void setMap(Map<Object, Object> in_map) { 
     map = in_map; 
    } 
} 

@Test 
public void test(){ 
    BingoFactory l_bingoFactory = new BingoFactory(); 

    Foo l_foo = new Foo(); 
    l_foo.setBingo(l_bingoFactory.createBingo());     // one in a field 
    l_foo.setWrapper(l_bingoFactory.createBingoWrapper());   // one in a field of a field 

    l_foo.setArray(new Bingo[]{l_bingoFactory.createBingo()});  // one in an array in a field 

    l_foo.setCollection(Arrays.asList(
      l_bingoFactory.createBingo(),       // one in Collection in a field 
      l_bingoFactory.createBingoWrapper()));     // one in a field of an item in a Collection in a field 

    Map<Object,Object> l_map = new HashMap<>(); 
    l_foo.setMap(l_map); 
    l_map.put("key", l_bingoFactory.createBingo());    // one as a key in a Map in a field 
    l_map.put(l_bingoFactory.createBingo(), "value");    // one as a value in a Map in a field 
    l_map.put("keyAgain", l_bingoFactory.createBingoWrapper()); // one wrapped in a value in a Map in a Field 
    l_map.put(l_bingoFactory.createBingoWrapper(), "valueAgain"); // one wrapped in a key in a Map in a field 

    List<Bingo> l_found = BeanUtils.scanObjectForType(l_foo, Bingo.class); // Magic happens here 

    System.out.println(l_found);         // for debug 
    Assert.assertTrue(l_found.containsAll(l_bingoFactory.getAllBingos())); // I want them ALL 
} 

回答

0

Spring's BeanUtils A液:(我已經添加一個布爾來決定,而需要輸入類的對象要被掃描或沒有。 (即你希望你的Bingo對象包含Bingo類型的其他對象?))

public static <T> List<T> scanObjectForType(Object in_object, Class<T> in_type, boolean in_scanSameType){ 
    return scanObjectForType(in_object, in_type, in_scanSameType, new HashSet<>()); 
} 

private static <T> List<T> scanObjectForType(Object in_object, Class<T> in_type, boolean in_scanSameType, Set<Object> in_alreadyScanned){ 
    if(in_type == null){ 
     throw new IllegalArgumentException("in_type should not be null"); 
    } 
    if(in_object instanceof Class){ 
     throw new IllegalArgumentException("in_type should not be a Class"); 
    } 
    if(in_object == null || in_alreadyScanned.contains(in_object)){ 
     return Collections.emptyList(); 
    } 
    in_alreadyScanned.add(in_object); // to prevent infinite loop when inner object references outer object 

    if(in_type.isInstance(in_object)){ 
     return Collections.singletonList((T) in_object); 
    } 

    List<T> l_result = new ArrayList<>(); 
    if(in_type.isInstance(in_object)){ 
     l_result.add((T) in_object); 
     if(!in_scanSameType){ 
      return l_result; 
     } 
    } 
    if(in_object instanceof Object[]){ 
     for(Object l_item : (Object[]) in_object){ 
      l_result.addAll(scanObjectForType(l_item, in_type, in_scanSameType, in_alreadyScanned)); 
     } 
    } else if(in_object instanceof Collection){ 
     for(Object l_item : (Collection<Object>) in_object){ 
      l_result.addAll(scanObjectForType(l_item, in_type, in_scanSameType, in_alreadyScanned)); 
     } 
    } else if(in_object instanceof Map){ 
     Map<Object,Object> l_map = (Map<Object,Object>) in_object; 
     for(Map.Entry<Object, Object> l_entry : l_map.entrySet()){ 
      l_result.addAll(scanObjectForType(l_entry.getKey(), in_type, in_scanSameType, in_alreadyScanned));  
      l_result.addAll(scanObjectForType(l_entry.getValue(), in_type, in_scanSameType, in_alreadyScanned)); 
     } 
    } else { 
     PropertyDescriptor[] l_descriptors = org.springframework.beans.BeanUtils.getPropertyDescriptors(in_object.getClass()); 
     for(PropertyDescriptor l_descriptor : l_descriptors){ 
      Method l_readMethod = l_descriptor.getReadMethod(); 
      if(l_readMethod != null){ 
       try { 
        Object l_readObject = l_readMethod.invoke(in_object); 
        if(l_readObject != null 
          && !l_readObject.equals(in_object)  // prevents infinite loops 
          && !(l_readObject instanceof Class)){ // prevents weird loops when accessing properties of classes 
         l_result.addAll(scanObjectForType(l_readObject,in_type, in_scanSameType, in_alreadyScanned)); 
        } 
       } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { 
        // too bad but continue 
        LOGGER.warn("Got an error trying to access field : ", e); 
        continue; 
       } 
      } 
     } 
    } 
    return l_result; 
} 

其侷限性:

  • 只使用公共訪問器掃描屬性
  • 不掃描Class類型(以防止掃描整個ClassLoader的類,並且因爲用例是DTO導向的)。
  • 依賴於遞歸性。我想實現一個BeanVisitor對象可能更漂亮,該對象在嵌套的bean的Set上循環操作。
  • 將掃描可能不屬於屬性的getter方法返回的對象。
  • 它沒有通過繼承進行測試。