Project Configuration with Spring

Table of Contents

1. Overview of the problem

Configuration must be environment specific – that’s just a fact of life. If that weren’t the case, then it wouldn’t be configuration and we would just hardcode values in code.

So – being able to set up configuration that is specific to the environment the application runs on is something that most projects have to solve one way or another.

For a Spring application there are several alternatives for addressing this problem, varying from simple and flexible solutions all the way to absolute nightmares of unnecessary abstraction. One of the more straightforward ways to address the problem in a flexible and manageable way is to use properties files and the first class property support provided by Spring.

As a proof of concept, for the purposes of this article, I will discuss the configuration of persistence in an application.

In this case, the properties will represent database credentials and URIs – for instance, we will want to be able to go to a production database in the production environment and to an in memory database for the dev environment. Other persistence related configuration may be the Hibernate dialect in case the application is using Hibernate or the JPA dialect for JPA – these should actually be sufficient to switch between persistence providers completely.

2. The .properties files

Let’s start by defining the environments we want to target:

  • Dev
  • Staging
  • Production

We now need to create the following properties files: persistence-dev.properties, persistence-staging.properties and persistence-production.properties. In a typical Maven application, these can reside in src/main/resources, but the wherever they are, they will need to be available on the classpath when the application is deployed.

An important thing to realize here is that having all properties files under version control makes configuration much more transparent and reproducible. This is in opposition to having the configs on disk somewhere and simply pointing Spring to that file.

3. Spring configuration

In Spring, the correct properties file needs to be used based on the environment:

<?xml version="1.0" encoding="UTF-8"?>
<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"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context-3.2.xsd">

      <context:property-placeholder
         location="
         classpath*:*persistence-${envTarget}.properties" />

</beans>

If Spring 3.1 is used, this configuration will create a PropertySourcesPlaceholderConfigurer which will add the properties defined in the file to the Spring Environment abstraction. For older versions of Spring, the older PropertyPlaceholderConfigurer will be created for dealing with the properties. For more details on the newly introduced Environment abstraction and properties support, see the previous Properties in Spring article.

This approach allows for the flexibility of having multiple *.properties files for specific, focused purposes – the persistence related properties can be grouped together in their own persistence-*.properties file, while other concerns can have their own .properties files. The common properties that are global to the application but do not change based on the environment can simply be added in an unique common.properties file.

4. Setting the Property in each environment

The final, deployable war will contain all properties files – for persistence, the three variants of persistence-*.properties. Since the files are actually named differently, there is no fear of accidentally working against the wrong one – by setting the envTarget variable, we will strongly select a single instance of the multiple existing variants.

The envTarget variable can be set in the OS/environment or as a parameter to the JVM command line:

-DenvTarget=dev

5. Maven and testing

Some integration tests need to have persistence enabled, but obviously not the same provider/schema as a standard environment. The flexibility of the current solution is perfect for this use case – the Maven pom.xml will need to simply specify the envTarget variable for testing:

<plugin>
   <groupId>org.apache.maven.plugins</groupId>
   <artifactId>maven-surefire-plugin</artifactId>
   <configuration>
      <systemPropertyVariables>
         <persistenceTarget>h2_test</persistenceTarget>
      </systemPropertyVariables>
   </configuration>
</plugin>

The corresponding persistence-h2_test.properties file can be placed in src/test/resources so that it will only be used for testing and not unnecessarily included and deployed with the war at runtime.

6. Going further

There are several ways to improve on this solution if needed. One such way is to use a more complex encoding for the names of the properties files, specifying not just the environment in which they are to be used, but also more information (such as the persistence provider).

The names of the properties files should now be: persistence-h2.properties, persistence-mysql.properties or, even more specific: persistence-dev_h2.properties, persistence-staging_mysql.properties, persistence-production_amazonRDS.properties.

The advantage of such a naming convention – and it is just a convention as nothing changes in the overall approach – is simply transparency. It now becomes much clearer what the configuration does only by looking at the names:

  • persistence-dev_h2.properties: the persistence provider for the dev environment is a lightweight, in-memory H2 database
  • persistence-staging_mysql.properties: the persistence provider for the staging environment is a MySQL instance
  • persistence-production_amazon_rds.propertie: the persistence provider for the production environment is Amazon RDS

7. Conclusion

This article discusses a flexible solution for doing environment specific configuration in Spring. For a complete configuration using this method, check out REST github project.

Finally, an alternative solution using profiles can be found here.

I usually post about Spring stuff on Google+ - you can follow me there:

Free eBook - REST Services with Spring
Join more than 2,200 engineers!

  • http://javarevisited.blogspot.com/2011/12/how-to-traverse-or-loop-hashmap-in-java.html javin @ loop map in java

    Nice post and thanks for sharing. Using IDE like Eclipse or Netbeans and using Maven for managing dependencies and library is my preferred way.I have blog my experience on spring as Ldap authentication using Spring with Example let me know how do you find it.

  • Leandro de Oliveira

    You are duplicating the same thing among 3 files, it’s better to have only one persistence.properties file and configure your build to change it according to the environment you’re building for. You should take a look at http://code.google.com/p/maven-config-processor-plugin/ to do this. This way you can even keep your production configuration in another place and have the plugin apply it at build time only, making your production env more secure.

    • baeldung

      Yes, I considered introducing this kind of details in the post. I have used these kinds of Maven magic in the past and it was always never the right approach. The downside of having the properties replicated into 3 files is likely minimal (keep in mind that the common stuff, that doesn’t change should be included into a common.properties file). The upside of doing things this way is that the solution is SIMPLE and easily understood, while a custom maven plugin doing transformation is not. For me, this is hugely important – I would much rather work with plain text files and keep things transparent and clear for ops and everybody much more than I would want to reduce redundancy to the bare possible minimum. That being said, I’m sure that there are some projects where the trade-offs are not as clear, or where personal preference is towards the Maven solution, it’s just that in my experience that hasn’t been the case.
      Thanks for the interesting feedback.
      Eugen.

      • Leandro de Oliveira

        Your solution is simpler for devs but isn’t for ops because the program will expose three config files to them and requires a JVM parameter.
        The build-time solution requires a config file for transformations and a pom.xml change, your solution also requires a change to make tests use the correct properties file. That may force people to configure JUnit executions in their IDE instead of just clicking “Run JUnit”. Also, this solution will present ops with just one config file instead of three.
        The less parameters you have to pass to your program, the less complicated it’ll be to run it from your IDE or send it over to operations run it.

        • baeldung

          Agreed – a parameter will need to be passed to the JVM when running the tests (which is something very standard in any IDE).
          Regarding the pom.xml change – there are pom changes and then there are pom changes – setting a variable is one thing, introducing a custom plugin doing transformations is another ( setting a JVM parameter is hopefully something simple and familiar to both dev and ops ). So, while I understand the appeal of the Maven solution, it is one more moving part that you need to understand and debug when things go wrong; I’d much rather do that with plain text files than with a Maven plugin, but it’s clearly a decision to be evaluated based on the context, and a tread-off one way or another. My personal preference is towards this solution and I would choose the Maven plugin only if the complexity price would really be justified (perhaps on extremely large projects with lots and lots of changing configuration).

    • Dave

      We generally don’t think it’s a good idea to re-build for each target environment. We want to feel confident that the artifact tested in one environment (e.g. system test) is the same artifact tested in the next environment (e.g. UAT). So I actually think environment-specific properties files is the right way to go in this case.

  • SK

    If we have to change the production environment configuration (let’s say there is some change to the database host/port), does this mean that i have to rebuild my maven artifact with the new properties file?

    • SK

      Ah, never mind – I see your point about making the properties file available on the classpath of the application, thanks!

    • Eugen

      Well that depends on how you read/load the properties. In most cases (and by default in Spring) properties are loaded once when the Spring context is bootstrapped, so whatever you add later on, the running web application will not pick up. You can change values by hand and restart – no need to build again. Also, if you go once step forward, if you have a mechanism that listens on changes of these properties files (and reloads the values when they change) they you no longer need the restart (see what LiveRebel does for an example), but to answer your questions – no, you don’t need to rebuild – that’s the entire point of keeping these properties in properties files and not in code.
      Thanks for the feedback.
      Eugen.

  • MaciejWalkowiak

    With Spring 3.1 there were introduced useful feature called profiles. With that you can define active profile for your environment – like you did with envTarget system property: http://blog.springsource.org/2011/02/14/spring-3-1-m1-introducing-profile/ .

    Recently I have posted how it can be used and configured together with Tomcat – to make it easy to build one package for all environments and make environment choose confiuration: http://maciejwalkowiak.pl/blog/2012/03/27/spring-3-1-profiles-and-tomcat-configuration/

  • avi

    Is there any way we can update bean properties in application context using properties object instead of properties file.

    • baeldung

      What exactly do you mean by properties object? Do you mean defining the properties programatically?