Properties with Spring and Spring Boot.

1. Overview

This tutorial will show how to set up and use Properties in Spring – via XML and <property-placeholder> or Java configuration and @PropertySource.
Before Spring 3.1, adding new properties files into Spring and using property values wasn’t as flexible and as robust as it could be. Starting with Spring 3.1, the new Environment and PropertySourceabstractions have simplified then entire process.
Now, if you want to go beyond the how to use properties and into how to actually do project configuration for a Spring application, definitely explore this write-up as well.



2. Register a Properties File in XML

In XML, new properties files can be made accessible to Spring via the <context:property-placeholder … > namespace element:
1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>
   xsi:schemaLocation="
      <context:property-placeholder location="classpath:foo.properties" />
</beans>
The foo.properties file should be placed under /src/main/resources so that it will be available on the classpath at runtime.

2.1. Multiple <property-placeholder>

In case multiple <property-placeholder> elements are present in the Spring context, there are a few best practices that should be followed:
  • the order attribute needs to be specified to fix the order in which these are processed by Spring
  • all property placeholders minus the last one (highest order) should have ignore-unresolvable=”true” to allow the resolution mechanism to pass to others in the context without throwing an exception

3. Register a Properties File via Java Annotations

Spring 3.1 also introduces the new @PropertySource annotation, as a convenient mechanism for adding property sources to the environment. This annotation is to be used in conjunction with Java based configuration and the @Configuration annotation:
1
2
3
4
5
@Configuration
@PropertySource("classpath:foo.properties")
public class PropertiesWithJavaConfig {
    //...
}

One other very useful way of registering a new properties file is using a placeholder to allow you to dynamically select the right file at runtime; for example:
1
2
3
4
@PropertySource({
  "classpath:persistence-${envTarget:mysql}.properties"
})
...

4. Using / Injecting Properties

Injecting a property with the @Value annotation is straightforward:
1
2
@Value( "${jdbc.url}" )
private String jdbcUrl;
A default value of the property can also be specified:
1
2
@Value( "${jdbc.url:aDefaultUrl}" )
private String jdbcUrl;
Using properties in Spring XML configuration:
1
2
3
<bean id="dataSource">
  <property name="url" value="${jdbc.url}" />
</bean>
Both the older PropertyPlaceholderConfigurer and the new PropertySourcesPlaceholderConfigureradded in Spring 3.1 resolve ${…} placeholders within bean definition property values and @Valueannotations.
Finally – to obtain the value of a property with the new Environment API:
1
2
3
4
@Autowired
private Environment env;
...
dataSource.setUrl(env.getProperty("jdbc.url"));
A very important caveat here is that using <property-placeholder> will not expose the properties to the Spring Environment – this means that retrieving the value like this will not work – it will return null:
1
env.getProperty("key.something")

4.1 Properties Search Precedence

By default, in Spring 4, local properties are search last, after all, environment property sources, including properties files. A quick sidenote here is that local properties are properties that are configured manually/programmatically via the setter APIs in the base PropertiesLoaderSupport class (setProperties, setLocation, etc).
This behavior can be overridden via the localOverride property of the PropertySourcesPlaceholderConfigurer, which can be set to true to allow local properties to override file properties.
In Spring 3.0 and before, the old PropertyPlaceholderConfigurer also attempted to look for properties both in the manually defined sources as well as in the System properties. The lookup precedence was also customizable via the systemPropertiesMode property of the configurer:
  • never – Never check system properties
  • fallback (default) – Check system properties if not resolvable in the specified properties files
  • override – Check system properties first, before trying the specified properties files. This allows system properties to override any other property source.
Finally, note that in case a property is defined in two or more files defined via @PropertySource – the last definition will win and override the previous ones. This makes the exact property value hard to predict, so if overriding is important, the PropertySource API can be used instead.
Before going low level with the configuration, let’s do some practical work working with properties files.

5. Properties with Spring Boot

Before we go into more advanced configuration options for properties, let’s spend some time looking at the new properties support in Spring Boot.
Generally speaking, this new support involves less configuration compared to standard Spring, which is of course one of the main goals of Boot.

5.1. application.properties – the Default Property File

Boot applies it’s typical convention over configuration approach to property files. This means that we can simply put an “application.properties” file in our “src/main/resources” directory, and it will be auto-detected. We can then inject any loaded properties from it as normal.
So, by using this default file, we don’t have to explicitly register a PropertySource, or even provide a path to a property file.
We can also configure a different file at runtime if we need to, using an environment property:
1
java -jar app.jar --spring.config.location=classpath:/another-location.properties

5.2. Environment Specific Properties File

If we need to target different environments, there’s a build-in mechanism for that in Boot.
We can simply define an “application-environment.properties” file in the “src/main/resources”directory – and then set a Spring profile with the same environment name.
For example, if we define a “staging” environment, that means we’ll have to define a staging profile and then application-staging.properties.
This env file will be loaded and will take precedence over the default property file. Note that the default file will still be loaded, it’s just that when there is a property collision the environment specific property file takes precedence.

5.3. Test Specific Properties File

We might also have a requirement to use different property values when our application is under test.
Spring Boot handles this for us by looking in our “src/test/resources” directory during a test run. Again, default properties will still be injectable as normal, but will be overridden these ones if there is a collision.

5.4. The @TestPropertySource Annotation

If we need more granular control over test properties, then we can make use of the @TestPropertySource annotation.
This allows us to set test properties for a specific test context, taking precedence over the default property sources:
1
2
3
4
5
@ContextConfiguration
@TestPropertySource("/my-test.properties")
public class IntegrationTests {
    // tests
}
If we don’t want to use a file, we can specify names and values directly:
1
2
3
4
5
@ContextConfiguration
@TestPropertySource("foo=bar", "bar=foo")
public class IntegrationTests {
    // tests
}
We can also achieve a similar effect using the properties argument of the @SpringBootTestannotation:
1
2
3
4
@SpringBootTest(properties = {"foo=bar", "bar=foo"})
public class IntegrationTests {
    // tests
}

5.5. Hierarchical Properties

If we have properties which are grouped together, we can make use of the @ConfigurationPropertiesannotation which will map these property hierarchies into Java objects graphs.
Let’s take some properties used to configure a database connection:
1
2
3
database.url=jdbc:postgresql:/localhost:5432/instance
database.username=foo
database.password=bar
And then let’s use the annotation to map them to a database object:
1
2
3
4
5
6
7
8
@ConfigurationProperties(prefix = "database")
public class Database {
    String url;
    String username;
    String password;
    // standard getters and setters
}
Spring Boot applies it’s convention over configuration approach again, automatically mapping between property names and their corresponding fields. All that we need to supply is the property prefix.
If you want to dig deeper into configuration properties, have a look at the in-depth article.

5.6. Alternative – YAML Files

YAML files are also supported.
All the same naming rules apply for test specific, environment specific, and default property files. The only difference is the file extension, and a dependency on the SnakeYAML library being on your classpath.
YAML is particularly good for hierarchical property storage; the following property file:
1
2
3
4
database.url=jdbc:postgresql:/localhost:5432/instance
database.username=foo
database.password=bar
secret: foo
Is synonymous to the following YAML file:
1
2
3
4
5
database:
  url: jdbc:postgresql:/localhost:5432/instance
  username: foo
  password: bar
secret: foo
It’s also worth mentioning that YAML files do not support the @PropertySource annotation, so if use of the annotation was required it would constrain us to using a properties file.

5.7. Properties from Command Line Arguments

As opposed to using files, properties can be passed directly on the command line:
1
java -jar app.jar --property="value"
You can also do this via system properties, which are provided before the -jar command rather than after it:
1
java -Dproperty.name="value" -jar app.jar

5.8. Properties from Environment Variables

Spring Boot will also detect environment variables, treating them as properties:
1
2
export name=value
java -jar app.jar

5.9 Randomization of Property Values

If we don’t want determinist property values, RandomValuePropertySource can be used to randomise the values of properties:
1
2
3
random.number=${random.int}
random.long=${random.long}
random.uuid=${random.uuid}

5.10. Additional Types of Property Sources

Spring Boot supports a multitude of property sources, implementing a well thought ordering to allow sensible overriding. It’s worth consulting the official documentation, which goes further than the scope of this article.

6. Behind the Scenes – the Spring Configuration

6.1. Before Spring 3.1

Spring 3.1 introduced the convenient option of defining properties sources using annotations – but before that, XML Configuration was necessary for these.
The <context:property-placeholder> XML element automatically registers a new PropertyPlaceholderConfigurer bean in the Spring Context. For backward compatibility, this is also the case in Spring 3.1 and above if the XSD schemas are not yet upgraded to point to the new 3.1 XSD versions.

6.2. After Spring 3.1

From Spring 3.1 onward, the XML <context:property-placeholder> will no longer register the old PropertyPlaceholderConfigurer but the newly introduced PropertySourcesPlaceholderConfigurer. This replacement class was created to be more flexible and to better interact with the newly introduced Environment and PropertySource mechanism.
For applications using Spring 3.1 or above, this should be considered the standard.

7. Configuration using Raw Beans in Spring 3.0 – the PropertyPlaceholderConfigurer

Besides the convenient methods of getting properties into Spring – annotations and the XML namespace – the property configuration bean can also be defined and registered manually. Working with the PropertyPlaceholderConfigurer gives us full control over the configuration, with the downside of being more verbose and most of the time, unnecessary.

7.1. Java configuration

1
2
3
4
5
6
7
8
9
10
@Bean
public static PropertyPlaceholderConfigurer properties() {
  PropertyPlaceholderConfigurer ppc
    = new PropertyPlaceholderConfigurer();
  Resource[] resources = new ClassPathResource[]
    { new ClassPathResource( "foo.properties" ) };
  ppc.setLocations( resources );
  ppc.setIgnoreUnresolvablePlaceholders( true );
  return ppc;
}

7.2. XML configuration

1
2
3
4
5
6
7
8
9
<bean
  class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
        <list>
            <value>classpath:foo.properties</value>
        </list>
    </property>
    <property name="ignoreUnresolvablePlaceholders" value="true"/>
</bean>

8. Configuration using Raw Beans in Spring 3.1 – the PropertySourcesPlaceholderConfigurer

Similarly, in Spring 3.1, the new PropertySourcesPlaceholderConfigurer can also be configured manually:

8.1. Java configuration

1
2
3
4
5
6
7
8
9
10
@Bean
public static PropertySourcesPlaceholderConfigurer properties(){
  PropertySourcesPlaceholderConfigurer pspc
    = new PropertySourcesPlaceholderConfigurer();
  Resource[] resources = new ClassPathResource[ ]
    { new ClassPathResource( "foo.properties" ) };
  pspc.setLocations( resources );
  pspc.setIgnoreUnresolvablePlaceholders( true );
  return pspc;
}

8.2. XML configuration

1
2
3
4
5
6
7
8
9
<bean
  class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
    <property name="locations">
        <list>
            <value>classpath:foo.properties</value>
        </list>
    </property>
    <property name="ignoreUnresolvablePlaceholders" value="true"/>
</bean>

9. Properties in Parent-Child Contexts

This question keeps coming up again and again – what happens when your web application has a parent and a child context? The parent context may have some common core functionality and beans, and then one (or multiple) child contexts, maybe containing servlet specific beans.
In that case, what’s the best way to define properties files and include them into these contexts? What’s more – how to best retrieve these properties from Spring? Here is the simple breakdown.

9.1. If the properties file is defined in XML with <property-placeholder>

If the file is defined in the Parent context:
  • @Value works in Child context: NO
  • @Value works in Parent context: YES
If the file is defined in the Child context:
  • @Value works in Child context: YES
  • @Value works in Parent context: NO
Finally, as we discussed before, <property-placeholder> does not expose the properties to the environment, so:
  • environment.getProperty works in either context: NO

9.2. If the properties file is defined in Java with @PropertySource

If the file is defined in the Parent context:
  • @Value works in Child context: YES
  • @Value works in Parent context: YES
  • environment.getProperty in Child context: YES
  • environment.getProperty in Parent context: YES
If the file is defined in the Child context:
  • @Value works in Child context: YES
  • @Value works in Parent context: NO
  • environment.getProperty in Child context: YES
  • environment.getProperty in Parent context: NO

Reference:

Comments

Popular Posts