Reading a dynamic property map into Spring managed

2020-05-27 04:14发布

I have a properties file like this:

my.properties file:
app.One.id=1
app.One.val=60

app.Two.id=5
app.Two.val=75

And I read these values into a map property in my bean in Spring config file like this:

spring-config.xml:
<bean id="myBean" class="myClass" scope="singleton">
    <property name="myMap">
        <map>
            <entry key="${app.One.id}" value="${app.One.val}"/>
            <entry key="${app.Two.id}" value="${app.Two.val}"/>
        </map>
    </property>
</bean>

This way if I add a new id/val to the properties file, I must add a row in config xml in order to have the new id/val in myMap.

My question is, is there a way to specify the key-val pairs in spring config file so that the number of key-vals defined in xml can figure out the items in the properties file and create a map. Basically I want to use this xml file in different environments where we use different number of key-value items in properties file. I just don't want to change the xml file in each environment to read in all these values.

Let me know if you need any other details. Any thoughts/comments is appreciated. Thanks!

4条回答
别忘想泡老子
2楼-- · 2020-05-27 04:43

It's a classic environment problem.

There are two ways to do this:

  1. Add an environment string at the end of the appropriate .properties file; pass that value to the app when it starts and let Spring choose the correct one.
  2. Put those dynamic properties in a database and query for them on startup. The JNDI for the database will pick the right values.
查看更多
等我变得足够好
3楼-- · 2020-05-27 04:44

I did not find an ideal way to solve this issue to have dynamic map property read into spring configuration info. Using DB was also not an option for us. This is what I found to be the best alternative for this:

  • Use a standard format for map key/value pair in the properties file eg: key1 | val1, key2 | val2, key3 | val3, ..., keyn | valn

  • Read this to a String property or List property in configuration bean like here: Use String to List

  • Let injected java class split the items into map in setter.

I'm going to mark this as answer. If anyone else has better ways to do this, comment it out and I will change it to answer.

查看更多
贼婆χ
4楼-- · 2020-05-27 04:50

This is done with Spring EL and custom handling.

It was just interesting for me to try it. It works :)

application.xml

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd
    ">

    <util:properties id="values" location="values.properties" />

    <bean id="hello" class="my.Hello">
        <property name="map"
            value="#{T(my.Utils).propsToMap(values, '^(app\.\w*)\.id$', '{idGroup}.val')}" />
    </bean>

</beans>

Utils.java

package my;

import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Utils {

    public static Map<String, String> propsToMap(Properties props,
            String idPatternString, String valuePatternString) {

        Map<String, String> map = new HashMap<String, String>();

        Pattern idPattern = Pattern.compile(idPatternString);

        for (Enumeration<?> en = props.propertyNames(); en.hasMoreElements();) {
            String key = (String) en.nextElement();
            String mapKey = (String) props.getProperty(key);

            if (mapKey != null) {
                Matcher idMatcher = idPattern.matcher(key);
                if (idMatcher.matches()) {

                    String valueName = valuePatternString.replace("{idGroup}",
                            idMatcher.group(1));
                    String mapValue = props.getProperty(valueName);

                    if (mapValue != null) {
                        map.put(mapKey, mapValue);
                    }
                }
            }
        }

        return map;
    }
}

Hello.java

package my;

import java.util.Map;

public class Hello {

    private Map<String, String> map;

    public Map<String, String> getMap() {
        return map;
    }

    public void setMap(Map<String, String> map) {
        this.map = map;
    }
}

values.properties

app.One.id=1
app.One.val=60

app.Two.id=5
app.Two.val=75
查看更多
可以哭但决不认输i
5楼-- · 2020-05-27 05:04

Maybe I did not fully understand the issue here...
What about a simplified approach?

my.properties file:
1=60
5=75

Application Context

<bean id="myProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
    <property name="locations">
        <list>
        <value>classpath: my.properties</value>
        </list>
    </property>
</bean> 
<bean id="myClass" class="com.pakage.MyClass">
    <property name="myMap" ref=" myProperties"/>
</bean>

Java Bean

public class MyClass {
    private Map<String , String> myMap;

    public void setMyMap(Map<String, String> myMap) {
        this.myMap = myMap;
    }

    public Map<String , String> getMyMap(){
        return myMap;
    }
}
查看更多
登录 后发表回答