148

We use the code below to inject Spring beans with properties from a properties file.

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations" value="classpath:/my.properties"/>
</bean>

<bean id="blah" class="abc">
    <property name="path" value="${the.path}"/>
</bean>

Is there a way we can access the properties programmatically? I'm trying to do some code without dependency injection. So I'd like to just have some code like this:

PropertyPlaceholderConfigurer props = new PropertyPlaceholderConfigurer();
props.load("classpath:/my.properties");
props.get("path");
Cœur
  • 37,241
  • 25
  • 195
  • 267
Marcus Leon
  • 55,199
  • 118
  • 297
  • 429
  • A complete example of accessing properties file in spring is at the following link: http://bharatonjava.wordpress.com/2012/08/24/access-properties-file-values-in-spring-mvc-controller-class/ –  Aug 25 '12 at 08:44
  • This is why I don't like Spring. – Sridhar Sarnobat Jul 10 '23 at 23:32

16 Answers16

197

How about PropertiesLoaderUtils?

Resource resource = new ClassPathResource("/my.properties");
Properties props = PropertiesLoaderUtils.loadProperties(resource);
skaffman
  • 398,947
  • 96
  • 818
  • 769
  • 6
    here is a question, how is this different from mine, and has two more votes AND posted second... – Zoidberg Nov 20 '09 at 16:07
  • 3
    Beats me, I didn't get to vote :) I wouldn't use a `PropertyPlaceholderConfigurer`, though it's overkill for the task. – skaffman Nov 20 '09 at 16:15
  • 5
    I was trying to get as close to what he had as possible, I have been downvoted so many times for not providing enough detail. In anycase, your answers deserve the votes, as it is correct, I guess I am just jealous I didn't get 2 votes as well, LOL. – Zoidberg Nov 20 '09 at 17:07
  • 2
    what should we give in path if the file is placed in external directory, let's say config folder ? – prnjn Oct 05 '19 at 14:56
  • @prnjn I would assume 'new FileSystemResource(fullPath)' – Orubel May 03 '21 at 15:29
61

If all you want to do is access placeholder value from code, there is the @Value annotation:

@Value("${settings.some.property}")
String someValue;

To access placeholders From SPEL use this syntax:

#('${settings.some.property}')

To expose configuration to views that have SPEL turned off, one can use this trick:

package com.my.app;

import java.util.Collection;
import java.util.Map;
import java.util.Set;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.stereotype.Component;

@Component
public class PropertyPlaceholderExposer implements Map<String, String>, BeanFactoryAware {  
    ConfigurableBeanFactory beanFactory; 

    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = (ConfigurableBeanFactory) beanFactory;
    }

    protected String resolveProperty(String name) {
        String rv = beanFactory.resolveEmbeddedValue("${" + name + "}");

        return rv;
    }

    @Override
    public String get(Object key) {
        return resolveProperty(key.toString());
    }

    @Override
    public boolean containsKey(Object key) {
        try {
            resolveProperty(key.toString());
            return true;
        }
        catch(Exception e) {
            return false;
        }
    }

    @Override public boolean isEmpty() { return false; }
    @Override public Set<String> keySet() { throw new UnsupportedOperationException(); }
    @Override public Set<java.util.Map.Entry<String, String>> entrySet() { throw new UnsupportedOperationException(); }
    @Override public Collection<String> values() { throw new UnsupportedOperationException(); }
    @Override public int size() { throw new UnsupportedOperationException(); }
    @Override public boolean containsValue(Object value) { throw new UnsupportedOperationException(); }
    @Override public void clear() { throw new UnsupportedOperationException(); }
    @Override public String put(String key, String value) { throw new UnsupportedOperationException(); }
    @Override public String remove(Object key) { throw new UnsupportedOperationException(); }
    @Override public void putAll(Map<? extends String, ? extends String> t) { throw new UnsupportedOperationException(); }
}

And then use the exposer to expose properties to a view:

<bean class="org.springframework.web.servlet.view.UrlBasedViewResolver" id="tilesViewResolver">
    <property name="viewClass" value="org.springframework.web.servlet.view.tiles2.TilesView"/>
    <property name="attributesMap">
        <map>
            <entry key="config">
                <bean class="com.my.app.PropertyPlaceholderExposer" />
            </entry>
        </map>
    </property>
</bean>

Then in view, use the exposed properties like this:

${config['settings.some.property']}

This solution has the advantage that you can rely on standard placeholder implementation injected by the context:property-placeholder tag.

Now as a final note, if you really need a to capture all placeholder properties and their values, you have to pipe them through StringValueResolver to make sure that placeholders work inside the property values as expected. The following code will do that.

package com.my.app;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.util.StringValueResolver;

public class AppConfig extends PropertyPlaceholderConfigurer implements Map<String, String> {

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

    @Override
    protected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props)
            throws BeansException {

        this.props.clear();
        for (Entry<Object, Object> e: props.entrySet())
            this.props.put(e.getKey().toString(), e.getValue().toString());

        super.processProperties(beanFactory, props);
    }

    @Override
    protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
            StringValueResolver valueResolver) {

        super.doProcessProperties(beanFactoryToProcess, valueResolver);

        for(Entry<String, String> e: props.entrySet())
            e.setValue(valueResolver.resolveStringValue(e.getValue()));
    }

    // Implement map interface to access stored properties
    @Override public Set<String> keySet() { return props.keySet(); }
    @Override public Set<java.util.Map.Entry<String, String>> entrySet() { return props.entrySet(); }
    @Override public Collection<String> values() { return props.values(); }
    @Override public int size() { return props.size(); }
    @Override public boolean isEmpty() { return props.isEmpty(); }
    @Override public boolean containsValue(Object value) { return props.containsValue(value); }
    @Override public boolean containsKey(Object key) { return props.containsKey(key); }
    @Override public String get(Object key) { return props.get(key); }
    @Override public void clear() { throw new UnsupportedOperationException(); }
    @Override public String put(String key, String value) { throw new UnsupportedOperationException(); }
    @Override public String remove(Object key) { throw new UnsupportedOperationException(); }
    @Override public void putAll(Map<? extends String, ? extends String> t) { throw new UnsupportedOperationException(); }
}
kryger
  • 12,906
  • 8
  • 44
  • 65
anttix
  • 7,709
  • 1
  • 24
  • 25
  • Thnx for this very complete answer! is there a way to do this with final fields? – Ward May 19 '14 at 13:21
  • 2
    @WardC you can't inject into a final field. However you can inject to a constructor argument and set a final field value inside the constructor. See http://stackoverflow.com/questions/2306078/spring-constructor-injection-of-primitive-values-properties-with-annotation-b/2306468#2306468 and http://stackoverflow.com/questions/4203302/how-to-inject-a-value-to-bean-constructor-using-annotations – anttix May 19 '14 at 18:31
  • I tried the SpEL way but it throws `'Invalid collection name: #('${producer.mongodb.collection}')'`. I configured as: `@Document(collection = "#('${producer.mongodb.collection}')")` – CᴴᴀZ Nov 18 '21 at 13:15
  • Not sure what provides the annotation and if it supports placeholders, but I would try ```@Document(collection = "${producer.mongodb.collection}")``` – anttix Mar 27 '22 at 09:02
51

I have done this and it has worked.

Properties props = PropertiesLoaderUtils.loadAllProperties("my.properties");
PropertyPlaceholderConfigurer props2 = new PropertyPlaceholderConfigurer();
props2.setProperties(props);

That should work.

Zoidberg
  • 10,137
  • 2
  • 31
  • 53
51

CREDIT: Programmatic access to properties in Spring without re-reading the properties file

I've found a nice implementation of accessing the properties programmatically in spring without reloading the same properties that spring has already loaded. [Also, It is not required to hardcode the property file location in the source]

With these changes, the code looks cleaner & more maintainable.

The concept is pretty simple. Just extend the spring default property placeholder (PropertyPlaceholderConfigurer) and capture the properties it loads in the local variable

public class SpringPropertiesUtil extends PropertyPlaceholderConfigurer {

    private static Map<String, String> propertiesMap;
    // Default as in PropertyPlaceholderConfigurer
    private int springSystemPropertiesMode = SYSTEM_PROPERTIES_MODE_FALLBACK;

    @Override
    public void setSystemPropertiesMode(int systemPropertiesMode) {
        super.setSystemPropertiesMode(systemPropertiesMode);
        springSystemPropertiesMode = systemPropertiesMode;
    }

    @Override
    protected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props) throws BeansException {
        super.processProperties(beanFactory, props);

        propertiesMap = new HashMap<String, String>();
        for (Object key : props.keySet()) {
            String keyStr = key.toString();
            String valueStr = resolvePlaceholder(keyStr, props, springSystemPropertiesMode);
            propertiesMap.put(keyStr, valueStr);
        }
    }

    public static String getProperty(String name) {
        return propertiesMap.get(name).toString();
    }

}

Usage Example

SpringPropertiesUtil.getProperty("myProperty")

Spring configuration changes

<bean id="placeholderConfigMM" class="SpringPropertiesUtil">
    <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
    <property name="locations">
    <list>
        <value>classpath:myproperties.properties</value>
    </list>
    </property>
</bean>

Hope this helps to solve the problems you have

TheJeff
  • 3,665
  • 34
  • 52
Ashok Koyi
  • 5,327
  • 8
  • 41
  • 50
  • 9
    This is not a full implementation and will not work correctly. The PropertyPlaceholderConfigurer uses a PropertyPlaceholderHelper to replace ALL placeholder properties, including nested placeholders. In Kalinga's implementation if you have something like myFile=${myFolder}/myFile.txt, the literal property value you'll get from the map using the key "myFile" will be ${myFolder}/myFile.txt. –  Mar 20 '12 at 15:42
  • 1
    This is the correct solution. To address Brian's concern. The ${myFolder} should be a system property and not be in the properties file. This can be solved by having the tomcat system property or run property set in eclipse. You might even be able to have a build property. This solution is assuming that a little and should address that but at the same time this answer is much more along the line of standard practice to load spring and java properties in one place instead of separately. Another option is to load a general properties file with myFile in the file and use that to get the rest. – Rob Dec 23 '14 at 15:22
  • 2
    I tried to apply this workaround to the 'new' PropertySourcesPlaceholderConfigurer from Spring 3.1+ but I've found that the method processProperties(ConfigurableListableBeanFactory beanFactory, Properties props) is now deprecated and therefore now there's no access to the 'props' argument. Looking at the sources of PropertySourcesPlaceholderConfigurer can't found a clean way to expose the properties. Any ideas to do it? Thanks! – Jorge Palacio Apr 09 '15 at 20:31
28

You can also use either the spring utils, or load properties via the PropertiesFactoryBean.

<util:properties id="myProps" location="classpath:com/foo/myprops.properties"/>

or:

<bean id="myProps" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
  <property name="location" value="classpath:com/foo/myprops.properties"/>
</bean>

Then you can pick them up in your application with:

@Resource(name = "myProps")
private Properties myProps;

and additionally use these properties in your config:

<context:property-placeholder properties-ref="myProps"/>

This is also in the docs: http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#xsd-config-body-schemas-util-properties

potame
  • 7,597
  • 4
  • 26
  • 33
enkor
  • 7,527
  • 3
  • 31
  • 55
10

Create a class like below

    package com.tmghealth.common.util;

    import java.util.Properties;

    import org.springframework.beans.BeansException;

    import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

    import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;

    import org.springframework.context.annotation.Configuration;

    import org.springframework.context.annotation.PropertySource;

    import org.springframework.stereotype.Component;


    @Component
    @Configuration
    @PropertySource(value = { "classpath:/spring/server-urls.properties" })
    public class PropertiesReader extends PropertyPlaceholderConfigurer {

        @Override
        protected void processProperties(
                ConfigurableListableBeanFactory beanFactory, Properties props)
                throws BeansException {
            super.processProperties(beanFactory, props);

        }

    }

Then wherever you want to access a property use

    @Autowired
        private Environment environment;
    and getters and setters then access using 

    environment.getProperty(envName
                    + ".letter.fdi.letterdetails.restServiceUrl");

-- write getters and setters in the accessor class

    public Environment getEnvironment() {
            return environment;
        }`enter code here`

        public void setEnvironment(Environment environment) {
            this.environment = environment;
        }
potame
  • 7,597
  • 4
  • 26
  • 33
user1419261
  • 829
  • 8
  • 5
9

You can get your properties through Environment class. As documentation stands:

Properties play an important role in almost all applications, and may originate from a variety of sources: properties files, JVM system properties, system environment variables, JNDI, servlet context parameters, ad-hoc Properties objects, Maps, and so on. The role of the environment object with relation to properties is to provide the user with a convenient service interface for configuring property sources and resolving properties from them.

Having Environment as a env variable, simply call:

env.resolvePlaceholders("${your-property:default-value}")

You can get your 'raw' properties through:

env.getProperty("your-property")

It will search through all properties source that spring has registered.

You can either obtain Environment through:

  • inject ApplicationContext by implementing ApplicationContextAware and then call getEnvironment() on context
  • implement EnvironmentAware.

It's obtain through implementation of a class because properties are resolved on early stage of application startup, as they may be required for bean construction.

Read more on documentation: spring Environment documentation

Augustin Ghauratto
  • 1,420
  • 1
  • 19
  • 21
4

As you know the newer versions of Spring don't use the PropertyPlaceholderConfigurer and now use another nightmarish construct called PropertySourcesPlaceholderConfigurer. If you're trying to get resolved properties from code, and wish the Spring team gave us a way to do this a long time ago, then vote this post up! ... Because this is how you do it the new way:

Subclass PropertySourcesPlaceholderConfigurer:

public class SpringPropertyExposer extends PropertySourcesPlaceholderConfigurer {

    private ConfigurableListableBeanFactory factory;

    /**
     * Save off the bean factory so we can use it later to resolve properties
     */
    @Override
    protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
            final ConfigurablePropertyResolver propertyResolver) throws BeansException {
        super.processProperties(beanFactoryToProcess, propertyResolver);

        if (beanFactoryToProcess.hasEmbeddedValueResolver()) {
            logger.debug("Value resolver exists.");
            factory = beanFactoryToProcess;
        }
        else {
            logger.error("No existing embedded value resolver.");
        }
    }

    public String getProperty(String name) {
        Object propertyValue = factory.resolveEmbeddedValue(this.placeholderPrefix + name + this.placeholderSuffix);
        return propertyValue.toString();
    }
}

To use it, make sure to use your subclass in your @Configuration and save off a reference to it for later use.

@Configuration
@ComponentScan
public class PropertiesConfig {

    public static SpringPropertyExposer commonEnvConfig;

    @Bean(name="commonConfig")
    public static PropertySourcesPlaceholderConfigurer commonConfig() throws IOException {
        commonEnvConfig = new SpringPropertyExposer(); //This is a subclass of the return type.
        PropertiesFactoryBean commonConfig = new PropertiesFactoryBean();
        commonConfig.setLocation(new ClassPathResource("META-INF/spring/config.properties"));
        try {
            commonConfig.afterPropertiesSet();
        }
        catch (IOException e) {
            e.printStackTrace();
            throw e;
        }
        commonEnvConfig.setProperties(commonConfig.getObject());
        return commonEnvConfig;
    }
}

Usage:

Object value = PropertiesConfig.commonEnvConfig.getProperty("key.subkey");
TheJeff
  • 3,665
  • 34
  • 52
3

This help me:

ApplicationContextUtils.getApplicationContext().getEnvironment()
Ru Yu
  • 51
  • 3
2

Here is another sample .

XmlBeanFactory factory = new XmlBeanFactory(new FileSystemResource("beans.xml"));
PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer();
cfg.setLocation(new FileSystemResource("jdbc.properties"));
cfg.postProcessBeanFactory(factory);
Brad Larson
  • 170,088
  • 45
  • 397
  • 571
Venky
  • 85
  • 1
2

This will resolve any nested properties.

public class Environment extends PropertyPlaceholderConfigurer {

/**
 * Map that hold all the properties.
 */
private Map<String, String> propertiesMap; 

/**
 * Iterate through all the Property keys and build a Map, resolve all the nested values before building the map.
 */
@Override
protected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props) throws BeansException {
    super.processProperties(beanFactory, props);

    propertiesMap = new HashMap<String, String>();
    for (Object key : props.keySet()) {
        String keyStr = key.toString();
        String valueStr = beanFactory.resolveEmbeddedValue(placeholderPrefix + keyStr.trim() + DEFAULT_PLACEHOLDER_SUFFIX);
        propertiesMap.put(keyStr, valueStr);
    }
} 

/**
 * This method gets the String value for a given String key for the property files.
 * 
 * @param name - Key for which the value needs to be retrieved.
 * @return Value
 */
public String getProperty(String name) {
    return propertiesMap.get(name).toString();
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Sohan
  • 21
  • 1
1

This post also explatis howto access properties: http://maciej-miklas.blogspot.de/2013/07/spring-31-programmatic-access-to.html

You can access properties loaded by spring property-placeholder over such spring bean:

@Named
public class PropertiesAccessor {

    private final AbstractBeanFactory beanFactory;

    private final Map<String,String> cache = new ConcurrentHashMap<>();

    @Inject
    protected PropertiesAccessor(AbstractBeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    public  String getProperty(String key) {
        if(cache.containsKey(key)){
            return cache.get(key);
        }

        String foundProp = null;
        try {
            foundProp = beanFactory.resolveEmbeddedValue("${" + key.trim() + "}");
            cache.put(key,foundProp);
        } catch (IllegalArgumentException ex) {
           // ok - property was not found
        }

        return foundProp;
    }
}
Maciej Miklas
  • 3,305
  • 4
  • 27
  • 52
1

This is the finest way I got it to work:

package your.package;

import java.io.IOException;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PropertiesLoaderUtils;

public class ApplicationProperties {

    private Properties properties;

    public ApplicationProperties() {
        // application.properties located at src/main/resource
        Resource resource = new ClassPathResource("/application.properties");
        try {
            this.properties = PropertiesLoaderUtils.loadProperties(resource);
        } catch (IOException ex) {
            Logger.getLogger(ApplicationProperties.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public String getProperty(String propertyName) {
        return this.properties.getProperty(propertyName);
    }
}
Daniel Almeida
  • 342
  • 2
  • 4
0
create .properties file in classpath of your project and add path configuration in xml`<context:property-placeholder location="classpath*:/*.properties" />`

in servlet-context.xml after that u can directly use your file everywhere

Aniket
  • 11
  • 1
0

Please use the below code in your spring configuration file to load the file from class path of your application

 <context:property-placeholder
    ignore-unresolvable="true" ignore-resource-not-found="false" location="classpath:property-file-name" />
Abhishek Jha
  • 111
  • 1
  • 3
0

I know this is an old thread, however, this topic in my opinion becomes of great importance for those using the functional approach for all those usecases where you need a microservice that loads "instantly" and therefore you avoid using annotations. The problem that remained unsolved was to load eventually the environment variables which I had in my application.yml.

public class AppPropsLoader {
public static Properties load() {
  var propPholderConfig = new PropertySourcesPlaceHolderConfigurer();
  var yaml = new YamlPropertiesFactoryBean();
  ClassPathResource resource = new ClassPathResource("application.yml");
  Objects.requireNonNull(resource, "File application.yml does not exist");
  yaml.setResources(resource);
  Objects.requireNonNull(yaml.getObject(), "Configuration cannot be null");
  propPholderConfig.postProcessBeanFactory(new DefaultListableBeanFactory());
  propPholderConfig.setProperties(yaml.getObject());
  PropertySources appliedPropertySources = 
  propPholderConfig.getAppliedPropertySources();
  var resolver = new PropertySourcesPlaceholderResolver(appliedPropertySources);
  Properties resolvedProps = new Properties();
  for (Map.Entry<Object, Object> prop: yaml.getObject().entrySet()) {
    resolvedProps.setProperty((String)prop.getKey(), 
      getPropertyValue(resolver.resolvePlaceHolders(prop.getValue()));
  }
  return resolvedProps;
}
 static String getPropertyValue(Object prop) {
   var val = String.valueOf(prop);
   Pattern p = Pattern.compile("^(\\$\\{)([a-zA-Z0-9-._]+)(\\})$");
   Matcher m = p.matcher(val);
   if(m.matches()) {
    return System.getEnv(m.group(2));
  }
   return val;
 }
}
Davide Pugliese
  • 366
  • 7
  • 16