Reading a dynamic property map into Spring managed bean

16,231

Solution 1

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

Solution 2

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;
    }
}

Solution 3

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.
Share:
16,231
kriver
Author by

kriver

Updated on July 19, 2022

Comments

  • kriver
    kriver almost 2 years

    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!

  • kriver
    kriver over 11 years
    We don't want to do this using DB. I was looking for something more like Here. There it's easier as it's just a list, but I was wondering if there is something equivalent to be used with maps?
  • duffymo
    duffymo over 11 years
    Since Property IS-A Map, I don't see why not. Try it.