web-dev-qa-db-de.com

So füllen Sie HashMap aus der Eigenschaftendatei Java mit Spring @Value

Ist es möglich, Spring @Value zu verwenden, um Werte aus der Eigenschaftendatei der HashMap zuzuordnen?.

Momentan habe ich so etwas und die Zuordnung eines Wertes ist kein Problem. Ich muss jedoch benutzerdefinierte Werte in HashMap-Ablaufdaten zuordnen. Ist so etwas möglich?

@Service
@PropertySource(value = "classpath:my_service.properties")
public class SomeServiceImpl implements SomeService {


    @Value("#{conf['service.cache']}")
    private final boolean useCache = false;

    @Value("#{conf['service.expiration.[<custom name>]']}")
    private final HashMap<String, String> expirations = new HashMap<String, String>();

Eigenschaftendatei: 'my_service.properties'

service.cache=true
service.expiration.name1=100
service.expiration.name2=20

Ist es möglich, wie folgt abzubilden: Wert gesetzt

  • name1 = 100

  • name2 = 20

34
d-sauer

Ich mache eine Lösung, die vom vorherigen Beitrag inspiriert ist.

Registrieren Sie die Eigenschaftendatei in der Spring-Konfiguration:

<util:properties id="myProp" location="classpath:my.properties"/>

Und ich erstelle Komponente:

@Component("PropertyMapper")
public class PropertyMapper {

    @Autowired
    ApplicationContext applicationContext;

    public HashMap<String, Object> startWith(String qualifier, String startWith) {
        return startWith(qualifier, startWith, false);
    }

    public HashMap<String, Object> startWith(String qualifier, String startWith, boolean removeStartWith) {
        HashMap<String, Object> result = new HashMap<String, Object>();

        Object obj = applicationContext.getBean(qualifier);
        if (obj instanceof Properties) {
            Properties mobileProperties = (Properties)obj;

            if (mobileProperties != null) {
                for (Entry<Object, Object> e : mobileProperties.entrySet()) {
                    Object oKey = e.getKey();
                    if (oKey instanceof String) {
                        String key = (String)oKey;
                        if (((String) oKey).startsWith(startWith)) {
                            if (removeStartWith) 
                                key = key.substring(startWith.length());
                            result.put(key, e.getValue());
                        }
                    }
                }
            }
        }

        return result;
    }
}

Und wenn ich HashMap alle Eigenschaften zuordnen möchte, die mit einem bestimmten Wert beginnen, mit der Annotation @Value:

@Service
public class MyServiceImpl implements MyService {

    @Value("#{PropertyMapper.startWith('myProp', 'service.expiration.', true)}")
    private HashMap<String, Object> portalExpirations;
15
d-sauer

Ist es möglich, Spring @Value zu verwenden, um Werte aus der Eigenschaftendatei der HashMap zuzuordnen?

Ja ist es. Mit ein wenig Code und Spel .

Betrachten Sie zuerst diese singleton Spring-Bean (Sie sollten sie scannen):

@Component("PropertySplitter")
public class PropertySplitter {

    /**
     * Example: one.example.property = KEY1:VALUE1,KEY2:VALUE2
     */
    public Map<String, String> map(String property) {
        return this.map(property, ",");
    }

    /**
     * Example: one.example.property = KEY1:VALUE1.1,VALUE1.2;KEY2:VALUE2.1,VALUE2.2
     */
    public Map<String, List<String>> mapOfList(String property) {
        Map<String, String> map = this.map(property, ";");

        Map<String, List<String>> mapOfList = new HashMap<>();
        for (Entry<String, String> entry : map.entrySet()) {
            mapOfList.put(entry.getKey(), this.list(entry.getValue()));
        }

        return mapOfList;
    }

    /**
     * Example: one.example.property = VALUE1,VALUE2,VALUE3,VALUE4
     */
    public List<String> list(String property) {
        return this.list(property, ",");
    }

    /**
     * Example: one.example.property = VALUE1.1,VALUE1.2;VALUE2.1,VALUE2.2
     */
    public List<List<String>> groupedList(String property) {
        List<String> unGroupedList = this.list(property, ";");

        List<List<String>> groupedList = new ArrayList<>();
        for (String group : unGroupedList) {
            groupedList.add(this.list(group));
        }

        return groupedList;

    }

    private List<String> list(String property, String splitter) {
        return Splitter.on(splitter).omitEmptyStrings().trimResults().splitToList(property);
    }

    private Map<String, String> map(String property, String splitter) {
        return Splitter.on(splitter).omitEmptyStrings().trimResults().withKeyValueSeparator(":").split(property);
    }

}

Hinweis: Die Klasse PropertySplitter verwendet das Hilfsprogramm Splitter von Guava. Weitere Informationen finden Sie in Dokumentation .

Dann in einer Bohne von dir:

@Component
public class MyBean {

    @Value("#{PropertySplitter.map('${service.expiration}')}")
    Map<String, String> propertyAsMap;

}

Und schließlich die Eigenschaft:

service.expiration = name1:100,name2:20

Es ist nicht genau das, wonach Sie gefragt haben, denn dieses PropertySplitter funktioniert mit einer einzigen Eigenschaft, die transformiert in ein Map umgewandelt wird, aber ich denke, Sie könnten entweder wechseln Gehen Sie auf diese Art und Weise vor, um Eigenschaften anzugeben, oder ändern Sie den Code PropertySplitter so, dass er der gewünschten hierarchischen Art und Weise entspricht.

Sie können die SPEL json-ähnliche Syntax verwenden, um eine einfache Map oder eine Map der Liste in die Eigenschaftendatei zu schreiben.

simple.map={'KEY1': 'value1', 'KEY2': 'value3', 'KEY3': 'value5'}

map.of.list={\
  'KEY1': {'value1','value2'}, \
  'KEY2': {'value3','value4'}, \
  'KEY3': {'value5'} \
 }

Ich habe \ Für mehrzeilige Eigenschaften verwendet, um die Lesbarkeit zu verbessern

Dann können Sie in Java mit @Value Wie folgt automatisch darauf zugreifen und es analysieren.

@Value("#{${simple.map}}")
Map<String, String> simpleMap;

@Value("#{${map.of.list}}")
Map<String, List<String>> mapOfList;

Hier mit ${simple.map} Erhält @Value Die folgende Zeichenfolge aus der Eigenschaftendatei:

"{'KEY1': 'value1', 'KEY2': 'value3', 'KEY3': 'value5'}"

Dann wird es ausgewertet, als ob es eingebettet wäre

@Value("#{{'KEY1': 'value1', 'KEY2': 'value3', 'KEY3': 'value5'}}")

Weitere Informationen finden Sie in der offiziellen Dokumentation

15
Marc Bouvier

Ab Spring 4.1.x (ich kann mich jedoch nicht an eine bestimmte Version erinnern) können Sie so etwas tun

@Value("#{${your.properties.key.name}}")
private Map<String, String> myMap;

wobei your.properties.key.name in Ihrer Eigenschaftendatei ungefähr so ​​aussehen sollte

your.properties.key.name={\
    name1 : 100, \
    name2 : 200 \
}

Stellen Sie einfach sicher, dass Sie das Bean PropertySourcesPlaceholderConfigurer erstellen, damit es sowohl in Ihrer App als auch beim Schreiben von Unit-Test-Code zum Testen des Codes funktioniert. Andernfalls funktioniert der Platzhalter $ {...} für den Eigenschaftswert nicht wie erwartet und Sie werden einige seltsame SpringEL-Fehler sehen.

10
wonhee

Die schnellste auf Spring Boot basierende Lösung, die mir einfällt, ist die folgende. In meinem speziellen Beispiel migriere ich Daten von einem System auf ein anderes. Deshalb brauche ich ein Mapping für ein Feld namens priority.

Zuerst habe ich die Eigenschaftendatei (priority-migration.properties) wie folgt erstellt:

my.prefix.priority.0:0
my.prefix.priority.10:1
my.prefix.priority.15:2
my.prefix.priority.20:2
another.prefix.foo:bar

und lege es auf den Klassenpfad.

Angenommen, Sie möchten die Map in einer Spring-Managed-Bean/-Komponente verwenden, kommentieren Sie Ihre Klasse mit:

@Component
@PropertySource("classpath:/priority-migration.properties")

Was Sie tatsächlich in Ihrer Map wollen, sind natürlich nur die Schlüssel/Wert-Paare, denen my.prefix vorangestellt ist, d. H. Dieser Teil:

{
    0:0
    10:1
    15:2
    20:2
}

Um dies zu erreichen, müssen Sie Ihre Komponente mit Anmerkungen versehen

@ConfigurationProperties("my.prefix")

und erstelle einen Getter für das Prioritäts-Infix . Letzteres hat sich in meinem Fall als obligatorisch erwiesen (obwohl das Sring Doc sagt, dass es ausreicht, eine Eigenschaftspriorität zu haben und zu initialisieren es mit einem veränderlichen Wert)

private final Map<Integer, Integer> priorityMap = new HashMap<>();

public Map<Integer, Integer> getPriority() {
    return priorityMap;
}

Am Ende

Es sieht ungefähr so ​​aus:

@Component
@ConfigurationProperties("my.prefix")
@PropertySource("classpath:/priority-migration.properties")
class PriorityProcessor {

    private final Map<Integer, Integer> priorityMap = new HashMap<>();

    public Map<Integer, Integer> getPriority() {
        return priorityMap;
    }

    public void process() {

        Integer myPriority = priorityMap.get(10)
        // use it here
    }
}
7