2015-06-25 11 views
18

Tôi tự hỏi, nếu có cách chung để điền vào bản đồ với các thuộc tính bạn chỉ biết tiền tố.Sử dụng ConfigurationProperties để điền Bản đồ theo cách tổng quát

Giả sử có một loạt các thuộc tính như

namespace.prop1=value1 
namespace.prop2=value2 
namespace.iDontKnowThisNameAtCompileTime=anothervalue 

Tôi muốn có một cách chung chung để điền vào khách sạn này bên trong một bản đồ, một cái gì đó giống như

@Component 
@ConfigurationProperties("namespace") 
public class MyGenericProps { 
    private Map<String, String> propmap = new HashMap<String, String>(); 

    // setter and getter for propmap omitted 

    public Set<String> returnAllKeys() { 
     return propmap.keySet(); 
    } 
} 

Hoặc là có một thuận tiện cách thu thập tất cả các thuộc tính với một tiền tố nhất định, thay vì lặp lại trên tất cả các PropertySources trong môi trường?

Cảm ơn Hansjoerg

Trả lời

30

Chừng nào bạn hài lòng có mỗi tài sản bổ sung vào bản đồ, chứ không phải chỉ những người mà bạn không biết trước, bạn có thể làm điều này với @ConfigurationProperties. Nếu bạn muốn lấy tất cả mọi thứ đó là dưới namespace thì bạn cần phải sử dụng một tiền tố rỗng và cung cấp một getter cho một bản đồ có tên namespace:

@ConfigurationProperties("") 
public class CustomProperties { 

    private final Map<String, String> namespace = new HashMap<>(); 

    public Map<String, String> getNamespace() { 
     return namespace; 
    } 

} 

Xuân Boot sử dụng phương pháp getNamespace để lấy bản đồ để nó có thể thêm tính chất của nó. Với những thuộc tính:

namespace.a=alpha 
namespace.b=bravo 
namespace.c=charlie 

Bản đồ namespace sẽ chứa ba mục:

{a=alpha, b=bravo, c=charlie} 

Nếu các thuộc tính được lồng nhau sâu sắc hơn, ví dụ:

namespace.foo.bar.a=alpha 
namespace.foo.bar.b=bravo 
namespace.foo.bar.c=charlie 

Sau đó, bạn muốn sử dụng namespace.foo làm tiền tố và đổi tên namespacegetNamespace trên CustomProperties đến bargetBar tương ứng.

Lưu ý rằng bạn nên áp dụng @EnableConfigurationProperties cho cấu hình của mình để bật hỗ trợ cho @ConfigurationProperties. Sau đó bạn có thể tham khảo bất kỳ đậu mà bạn muốn được xử lý bằng chú thích rằng, hơn là cung cấp một phương pháp @Bean cho họ, hoặc sử dụng @Component có họ phát hiện bằng cách quét thành phần:

@SpringBootApplication 
@EnableConfigurationProperties(CustomProperties.class) 
public class YourApplication { 
    // … 
} 
+0

Bạn có thể mở rộng về việc tại sao chúng ta shouldn' t chú thích @ConfigurationProperties với @Component? Nó được thực hiện trong tài liệu khởi động.Bạn có nói rằng sở thích là sử dụng @EnableConfiguratinoProperties không? – jst

+0

Tôi đã vội vã và nên giải thích cho bản thân mình một chút tốt hơn. Những gì tôi viết được diễn đạt quá mạnh mẽ. Bạn nên sử dụng '@ EnableConfigurationProperties' để bật hỗ trợ cho các bean được chú thích' @ ConfigurationProperties'. Tại thời điểm đó, bạn có thể tham chiếu lớp 'annotatedPro' có chú thích của bạn cho các shortcut và tránh phải khai báo nó như là một bean. Đây là những gì chính bản thân nó khởi động trong mã riêng của nó. Tôi sẽ viết lại câu trả lời đó. –

1

tôi đã viết bản thân mình một lớp MapFilter để xử lý này một cách hiệu quả. Về cơ bản, bạn tạo một Map và sau đó lọc nó bằng cách chỉ định tiền tố cho khóa. Ngoài ra còn có một nhà xây dựng có một Properties để thuận tiện.

Lưu ý rằng điều này chỉ lọc bản đồ chính. Bất kỳ thay đổi nào được áp dụng cho bản đồ được lọc cũng được áp dụng cho bản đồ cơ sở, bao gồm cả xóa vv nhưng rõ ràng là những thay đổi đối với bản đồ chính sẽ không được phản ánh trong bản đồ được lọc cho đến khi có điều gì đó gây dựng lại.

Cũng rất dễ dàng (và hiệu quả) để lọc các bản đồ đã lọc.

public class MapFilter<T> implements Map<String, T> { 

    // The enclosed map -- could also be a MapFilter. 
    final private Map<String, T> map; 
    // Use a TreeMap for predictable iteration order. 
    // Store Map.Entry to reflect changes down into the underlying map. 
    // The Key is the shortened string. The entry.key is the full string. 
    final private Map<String, Map.Entry<String, T>> entries = new TreeMap<>(); 
    // The prefix they are looking for in this map. 
    final private String prefix; 

    public MapFilter(Map<String, T> map, String prefix) { 
     // Store my backing map. 
     this.map = map; 
     // Record my prefix. 
     this.prefix = prefix; 
     // Build my entries. 
     rebuildEntries(); 
    } 

    public MapFilter(Map<String, T> map) { 
     this(map, ""); 
    } 

    private synchronized void rebuildEntries() { 
     // Start empty. 
     entries.clear(); 
     // Build my entry set. 
     for (Map.Entry<String, T> e : map.entrySet()) { 
      String key = e.getKey(); 
      // Retain each one that starts with the specified prefix. 
      if (key.startsWith(prefix)) { 
       // Key it on the remainder. 
       String k = key.substring(prefix.length()); 
       // Entries k always contains the LAST occurrence if there are multiples. 
       entries.put(k, e); 
      } 
     } 

    } 

    @Override 
    public String toString() { 
     return "MapFilter (" + prefix + ") of " + map + " containing " + entrySet(); 
    } 

    // Constructor from a properties file. 
    public MapFilter(Properties p, String prefix) { 
     // Properties extends HashTable<Object,Object> so it implements Map. 
     // I need Map<String,T> so I wrap it in a HashMap for simplicity. 
     // Java-8 breaks if we use diamond inference. 
     this(new HashMap<String, T>((Map) p), prefix); 
    } 

    // Helper to fast filter the map. 
    public MapFilter<T> filter(String prefix) { 
     // Wrap me in a new filter. 
     return new MapFilter<>(this, prefix); 
    } 

    // Count my entries. 
    @Override 
    public int size() { 
     return entries.size(); 
    } 

    // Are we empty. 
    @Override 
    public boolean isEmpty() { 
     return entries.isEmpty(); 
    } 

    // Is this key in me? 
    @Override 
    public boolean containsKey(Object key) { 
     return entries.containsKey(key); 
    } 

    // Is this value in me. 
    @Override 
    public boolean containsValue(Object value) { 
     // Walk the values. 
     for (Map.Entry<String, T> e : entries.values()) { 
      if (value.equals(e.getValue())) { 
       // Its there! 
       return true; 
      } 
     } 
     return false; 
    } 

    // Get the referenced value - if present. 
    @Override 
    public T get(Object key) { 
     return get(key, null); 
    } 

    // Get the referenced value - if present. 
    public T get(Object key, T dflt) { 
     Map.Entry<String, T> e = entries.get((String) key); 
     return e != null ? e.getValue() : dflt; 
    } 

    // Add to the underlying map. 
    @Override 
    public T put(String key, T value) { 
     T old = null; 
     // Do I have an entry for it already? 
     Map.Entry<String, T> entry = entries.get(key); 
     // Was it already there? 
     if (entry != null) { 
      // Yes. Just update it. 
      old = entry.setValue(value); 
     } else { 
      // Add it to the map. 
      map.put(prefix + key, value); 
      // Rebuild. 
      rebuildEntries(); 
     } 
     return old; 
    } 

    // Get rid of that one. 
    @Override 
    public T remove(Object key) { 
     // Do I have an entry for it? 
     Map.Entry<String, T> entry = entries.get((String) key); 
     if (entry != null) { 
      entries.remove(key); 
      // Change the underlying map. 
      return map.remove(prefix + key); 
     } 
     return null; 
    } 

    // Add all of them. 
    @Override 
    public void putAll(Map<? extends String, ? extends T> m) { 
     for (Map.Entry<? extends String, ? extends T> e : m.entrySet()) { 
      put(e.getKey(), e.getValue()); 
     } 
    } 

    // Clear everything out. 
    @Override 
    public void clear() { 
     // Just remove mine. 
     // This does not clear the underlying map - perhaps it should remove the filtered entries. 
     for (String key : entries.keySet()) { 
      map.remove(prefix + key); 
     } 
     entries.clear(); 
    } 

    @Override 
    public Set<String> keySet() { 
     return entries.keySet(); 
    } 

    @Override 
    public Collection<T> values() { 
     // Roll them all out into a new ArrayList. 
     List<T> values = new ArrayList<>(); 
     for (Map.Entry<String, T> v : entries.values()) { 
      values.add(v.getValue()); 
     } 
     return values; 
    } 

    @Override 
    public Set<Map.Entry<String, T>> entrySet() { 
     // Roll them all out into a new TreeSet. 
     Set<Map.Entry<String, T>> entrySet = new TreeSet<>(); 
     for (Map.Entry<String, Map.Entry<String, T>> v : entries.entrySet()) { 
      entrySet.add(new Entry<>(v)); 
     } 
     return entrySet; 
    } 

    /** 
    * An entry. 
    * 
    * @param <T> 
    * 
    * The type of the value. 
    */ 
    private static class Entry<T> implements Map.Entry<String, T>, Comparable<Entry<T>> { 

     // Note that entry in the entry is an entry in the underlying map. 
     private final Map.Entry<String, Map.Entry<String, T>> entry; 

     Entry(Map.Entry<String, Map.Entry<String, T>> entry) { 
      this.entry = entry; 
     } 

     @Override 
     public String getKey() { 
      return entry.getKey(); 
     } 

     @Override 
     public T getValue() { 
      // Remember that the value is the entry in the underlying map. 
      return entry.getValue().getValue(); 
     } 

     @Override 
     public T setValue(T newValue) { 
      // Remember that the value is the entry in the underlying map. 
      return entry.getValue().setValue(newValue); 
     } 

     @Override 
     public boolean equals(Object o) { 
      if (!(o instanceof Entry)) { 
       return false; 
      } 
      Entry e = (Entry) o; 
      return getKey().equals(e.getKey()) && getValue().equals(e.getValue()); 
     } 

     @Override 
     public int hashCode() { 
      return getKey().hashCode()^getValue().hashCode(); 
     } 

     @Override 
     public String toString() { 
      return getKey() + "=" + getValue(); 
     } 

     @Override 
     public int compareTo(Entry<T> o) { 
      return getKey().compareTo(o.getKey()); 
     } 
    } 

    // Simple tests. 
    public static void main(String[] args) { 
     String[] samples = { 
      "Some.For.Me", 
      "Some.For.You", 
      "Some.More", 
      "Yet.More"}; 
     Map map = new HashMap(); 
     for (String s : samples) { 
      map.put(s, s); 
     } 
     Map all = new MapFilter(map); 
     Map some = new MapFilter(map, "Some."); 
     Map someFor = new MapFilter(some, "For."); 
     System.out.println("All: " + all); 
     System.out.println("Some: " + some); 
     System.out.println("Some.For: " + someFor); 
    } 
} 
Các vấn đề liên quan