8

For now I could inject values from property-files:

@Value("${aaa.prop}")
public String someProp;

But I want something more...

For example I have some property file:

aaa.props=p1,p2,p3
aaa.props.p1=qwe
aaa.props.p2=asd
aaa.props.p3=zxc

I know for sure, that it contains property aaa.props, and know nothing about other properties. And I want to get this properties in to map using code like this:

@Value ("${aaa.props}") 
public Map<String, String> someProps;

Resulting someProps: {p1=qwe,p2=asd,p3=zxc}

1
  • You may find answers to this question useful: stackoverflow.com/questions/9259819/… . Inject properties object as resource, parse list one and build your map accordingly. Commented Jan 17, 2013 at 12:43

5 Answers 5

5

Well I built a generic approach for you: a factory bean that creates a map by filtering another map (properties are a kind of map, after all).

Here's the factory bean:

public class FilteredMapFactoryBean<V> extends
    AbstractFactoryBean<Map<String, V>>{

    private Map<String, V> input;

    /**
     * Set the input map.
     */
    public void setInput(final Map<String, V> input){
        this.input = input;
    }

    /**
     * Set the string by which key prefixes will be filtered.
     */
    public void setKeyFilterPrefix(final String keyFilterPrefix){
        this.entryFilter = new EntryFilter<String, V>(){

            @Override
            public boolean accept(final Entry<String, V> entry){
                return entry.getKey().startsWith(keyFilterPrefix);
            }
        };
    }

    public static interface EntryFilter<EK, EV> {

        boolean accept(Map.Entry<EK, EV> entry);
    }

    /**
     * If a prefix is not enough, you can supply a custom filter.
     */
    public void setEntryFilter(final EntryFilter<String, V> entryFilter){
        this.entryFilter = entryFilter;
    }

    private EntryFilter<String, V> entryFilter;

    /**
     * {@inheritDoc}
     */
    @Override
    public Class<?> getObjectType(){
        return Map.class;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected Map<String, V> createInstance() throws Exception{
        final Map<String, V> map = new LinkedHashMap<String, V>();
        for(final Entry<String, V> entry : this.input.entrySet()){
            if(this.entryFilter == null || this.entryFilter.accept(entry)){
                map.put(entry.getKey(), entry.getValue());
            }
        }
        return map;
    }

}

Here is a spring bean definition file with some sample usage:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <!-- use System.getProperties() as input -->
    <bean class="spring.test.FilteredMapFactoryBean" id="javaMap">
        <property name="keyFilterPrefix" value="java." />
        <property name="input" value="#{T(java.lang.System).getProperties()}" />
    </bean>

    <!-- use custom properties as input -->
    <bean class="spring.test.FilteredMapFactoryBean" id="customMap">
        <property name="keyFilterPrefix" value="hello" />
        <property name="input">
            <props>
                <prop key="hello">Is it me you're looking for?</prop>
                <prop key="hello.again">Just called to say: hello.</prop>
                <prop key="hello.goodby">You say goodbye and I say hello</prop>
                <prop key="goodbye.blue.sky">Did-did-did-did-you hear the falling bombs?</prop>
                <prop key="goodbye.ruby.tuesday">Who could hang a name on you?</prop>
            </props>
        </property>
    </bean>

</beans>

And here is a test class:

public class Tester{

    @SuppressWarnings("unchecked")
    public static void main(final String[] args){
        final ApplicationContext context =
            new ClassPathXmlApplicationContext("classpath:spring/test/mapFactorybean.xml");

        final Map<String, String> javaMap =
            (Map<String, String>) context.getBean("javaMap");
        print("java.", javaMap);
        final Map<String, String> customMap =
            (Map<String, String>) context.getBean("customMap");
        print("hello.", customMap);

    }

    private static void print(final String prefix, final Map<String, String> map){
        System.out.println("Map of items starting with " + prefix);
        for(final Entry<String, String> entry : map.entrySet()){
            System.out.println("\t" + entry.getKey() + ":" + entry.getValue());
        }
        System.out.println("");
    }

}

The output is as expected:

Map of items starting with java.
    java.runtime.name:Java(TM) SE Runtime Environment
    java.vm.version:14.2-b01
    java.vm.vendor:Sun Microsystems Inc.
    java.vendor.url:http://java.sun.com/
    java.vm.name:Java HotSpot(TM) Client VM
    java.vm.specification.name:Java Virtual Machine Specification
    java.runtime.version:1.6.0_16-b01
    java.awt.graphicsenv:sun.awt.Win32GraphicsEnvironment
        [... etc]

Map of items starting with hello.
    hello.goodby:You say goodbye and I say hello
    hello:Is it me you're looking for?
    hello.again:Just called to say: hello.
Sign up to request clarification or add additional context in comments.

2 Comments

AbstractFactoryBean is singleton by default. Wouldn't it be a problem to use this bean several times (with different properties) in a context?
@naXa this answer is 4 years old. these days FactoryBeans are kinda deprecated. I'd go with \@Configuration classes instead
2

I'm afraid you can't, directly. But you can

  • implement ApplicationContextAware and set the ApplicationContext as a field in your bean.
  • in a @PostConstruct method call context.getBean("${aaa.props}")
  • parse the result manually and set it to the desired fields

1 Comment

Is there a way to get all properties to to find ones I need?
2

you can use @Value.

Properties file:

aaa.props={p1:'qwe',p2:'asd',p3:'zxc'}

Java code:

@Value("#{${aaa.props}}")
private Map<String,String> someProps;

1 Comment

This is what should get upvoted and accepted as the answer. Additionally, those who use Spring XML based configuration, could simply inject this using properties such like p:someProps="#{${aaa.props}}".
0

You could do something like this: Maven dependency

<dependency>
      <groupId>javax.annotation</groupId>
      <artifactId>javax.annotation-api</artifactId>
      <version>1.2</version>
    </dependency>

Add the import.

import javax.annotation.Resource;

...

@Resource (name="propertiesMapName") 
public Properties someProps;

In your spring xml application context:

<util:properties id="propertiesMapName" location="classpath:yourFile.properties"/>

You will need this namespace

xmlns:util="http://www.springframework.org/schema/util"

http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.1.xsd

1 Comment

can i know why i have -1 in the solution? This works and i think is a pretty clean way, i would like to understand what is wrong. Thanks
-1

The solution it's well defined on https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config

@ConfigurationProperties(prefix="my")
public class Config {

    private List<String> servers = new ArrayList<String>();

    public List<String> getServers() {
        return this.servers;
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.