How to load properties files into Spring and expose to the Java classes

IMPORTANT!
This blog has moved to http://blog.brasskazoo.com!

Loading properties files in the spring configuration

The issue:

Our webapp is using a spring and hibernate backend, and both spring configuration and Java classes need to access database connection properties.

Currently there is an application.properties file and a spring applicationContext.xml, both which contain the same information for the database connection.

Ideally, there should be only one configuration file to define the properties, which is used by both spring and Java.

Spring has the ability to load properties files as part of the application configuration, for use internally. The properties can be referenced within spring configuration files using ant-style placeholders, e.g. ${app.var}.

To solve our issue though we’ll also need to provide a populated bean that can be used by our production classes.

Here’s the properties file (application.properties):

appl.name=My Web Application
appl.home=/Users/webapp/application/

# Database properties
db.driver=org.hsqldb.jdbcDriver
db.name=v8max.db
db.url=jdbc:hsqldb:file://${appl.home}/database/${db.name}
db.user=SA
db.pass=

Spring loads the properties file using a bean of the type org.springframework.beans.factory.config.PropertyPlaceholderConfigurer, and then we can reference the properties directly in the spring configuration file.

Note that we can also use placeholders within the properties file, as in the db.url above – the PropertyPlaceholderConfigurer will resolve them too!

    <!-- Load in application properties reference -->
    <bean id="applicationProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="classpath:application.properties"/>
    </bean>

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="${db.driver}"/>
        <property name="url" value="${db.url}"/>
        <property name="username" value="${db.user}"/>
        <property name="password" value="${db.pass}"/>
    </bean>

So now we’ve removed the need to have literals in the spring config!

Exposing the spring properties bean in java

To allow our Java classes to access the properties from the same object as spring, we’ll need to extend the PropertyPlaceholderConfigurer so that we can provide a more convenient method for retrieving the properties (there is no direct method of retrieving properties!).

We can extend the spring provided class to allow us to reuse spring’s property resolver in our Java classes:

public class PropertiesUtil extends PropertyPlaceholderConfigurer {
   private static Map propertiesMap;

   @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();
            propertiesMap.put(keyStr, parseStringValue(props.getProperty(keyStr),
                props, new HashSet()));
        }
    }

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

If we now update the applicationProperties bean to use the PropertiesUtil class, we can use the static getProperty method to access the resolved properties via the same object as the spring configuration bean.

Of course, we could run into problems if a class tries to use PropertiesUtil before the spring context has been initialised. For example if you’re registering ServletContextListeners in your web.xml before configuring spring, you’ll get a NullPointerException if one of those classes tries to use PropertiesUtil. For that reason I’ve had to declare the spring context before any context listeners.

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/applicationContext.xml
        </param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- Other listeners -->

References

  1. http://www.jdocs.com/spring/1.2.8/org/springframework/beans/factory/config/PropertyPlaceholderConfigurer.html
  2. http://j2eecookbook.blogspot.com/2007/07/accessing-properties-loaded-via-spring.html
About these ads

5 responses to “How to load properties files into Spring and expose to the Java classes

  1. Thank you! I have been going mad to find something in the web like this.
    I thought there just was a direct method to retrieve properties!

  2. You need to cast the Object returned from “propertiesMap.get(name);” to a String object. =) Checkout man!

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

    We are creating a social network in our university and we’ve used your example to load properties in the adress http://www.fiufiuuu.com/

    Thanks a lot.

  3. Hey thanks for the share, this exactly what i need

  4. Mark Wilson

    Thanks for the tip. As of Spring 3.x parseStringValue is deprecated, so the code should be changed to:

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

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

    public class SpringPropertiesUtil extends PropertyPlaceholderConfigurer {

    private static Map propertiesMap;
    // Default as in PropertyPlaceholderConfigurer
    private int springSystemPropertiesMode = SYSTEM_PROPERTIES_MODE_FALLBACK;

    /**
    * Workaround: PropertyPlaceholderConfigurer.systemPropertiesMode is not directly accessible
    */
    @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 ();
    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);
    }
    }

  5. Or even much better/simpler :
    public class SpringPropertyUtils extends PropertyPlaceholderConfigurer {
    private static Map properties = new HashMap();

    @Override
    protected void loadProperties(final Properties props) throws IOException {
    super.loadProperties(props);
    for (final Object key : props.keySet()) {
    properties.put((String) key, props.getProperty((String) key));
    }
    }

    /**
    * Return a property loaded by the place holder.
    * @param name the property name.
    * @return the property value.
    */
    public static String getProperty(final String name) {
    return properties.get(name);
    }
    }