Spring Properties with @PropertySource Annotation

PropertySource is an abstract base class that represents a source of name value property pairs. @PropertySource is an annotation for declaring a PropertySource to Spring’s Environment.

In this Spring tutorial, we are going to discuss the following three topics in detail with examples,

settings file

Introduction to @PropertySource Annotation

Spring 3.1 introduced a new annotation @PropertySource to declare a property source to the Spring Environment. That is to use property values configured in external files like *.properties, *.txt etc in application. Spring 4.0 has updated this @PropertySource  annotation by adding one new property to work with some failure cases. That new property is “ignoreResourceNotFound”.

It is always better to store the configuration informations in a properties file instead of maintaining it in the Java file.

Configure Properties Pre Spring 3.1

Let us learn about configuring property source in pre Spring 3.1 project. There are mainly two approaches to configure properties.

Approach 1:

Use PropertySourcesPlaceholderConfigurer class to configure properties files in Spring Beans XML configuration file as shown below.

application.properties file will be,

restapi.url=http://devapp.com/restapi/results
<?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-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
        <list>
            <value>classpath:application.properties</value>
        </list>
    </property>
</bean>
</beans>
${restapi.url }

Approach 2:

Use <context:property-placeholder> tag in an XML configuration file.

Steps 1 and 2 above are same and the way we define in configuration file is different.

<?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-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:property-placeholder location="classpath:application.properties" />
</beans>

How to use @PropertySource Annotation

From Spring 3.1 we can go the annotation way to configure properties in Spring as it is convenient to use.

org.springframework.context.annotation
@Target(value=TYPE)
@Retention(value=RUNTIME)
@Documented
public @interface PropertySource

@Target(value=TYPE) means we can use this annotation only at Class level. We cannot use this for Field level, Method level and Constructor level.

Example

To define a single properties file

@Configuration
@PropertySource("classpath:default.properties")
public class RestAPIURLConfig { … }

or

@Configuration
@PropertySource(value="classpath:default.properties")
public class RestAPIURLConfig { … }

or

@Configuration
@PropertySource(value={"file://D:/SpringExamples/default.properties"})
public class RestAPIURLConfig {}

When We define property files using “classpath” as shown in above examples, it searches that file at project classpath and resolve all values.

To define multiple properties file

@Configuration
@PropertySource(value={"classpath:default.properties","classpath:config.properties"})
public class RestAPIURLConfig {}

NOTE:

When we define multiple property files using @PropertySource annotation, then order of those files is very important. For instance, take above example. If we define same property(key-value) pair in both default.properties and config.properties files, then config.properties overrides default.properties value.

Spring Load Properties Example

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.javapapers</groupId>
	<artifactId>Spring3.1PropertySource</artifactId>
	<version>1.0.0</version>
	<properties>
		<spring-framework.version>3.1.0.RELEASE</spring-framework.version>
		<junit.version>4.11</junit.version>
		<cglib.version>3.1</cglib.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>
		<dependency>
			<groupId>cglib</groupId>
			<artifactId>cglib</artifactId>
			<version>${cglib.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>${spring-framework.version}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>${junit.version}</version>
			<scope>test</scope>
		</dependency>
	</dependencies>
</project>

Create a POJO Class

package com.javapapers.model;

public class Resource {

    private String url;

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

}

Define our application properties file with all REST EndPoint URLs. Assume that we have one external system which exposes REST API for our applications. In real-time applications, we know that we use different URLs for different environments. In DEV or Unit Testing, we may use local or mock REST API to ease development. For that one, wewill use one default.properties file.

And we will have different external Systems to provide different REST EndPoint URLs for QA,PRE-PROD and PROD. For these environments, we use different file i.e. application.properties

Our main intention is that if a required property is available in application.properties file, then use it. If not available, then our Application should not throw any error. It should use default value available in default.properties file then interact with REST API.

default.properties

restapi.url=http://devapp.com/restapi/results

application.properties

##QA
#restapi.url=http://qaapp.com/restapi/results
##PRE-PROD
#restapi.url=http://preprodapp.com/restapi/results
##PROD
#restapi.url=http://prodapp.com/restapi/results

Here intentionally, I have commented all properties in application.properties file. We need to uncomment one required property as we move from DEV to QA to PRE-PROD to PROD environments and use that property value to interact with External REST API.

We will write some unit tests to test them.

Create a Spring Configuration class

RestAPIURLConfig class configures two property files at class level. It uses @Value annotation to assign value to restAPIUrl property.

We have written a method propertyConfig() to create an instance of PropertySourcesPlaceholderConfigurer class. Like Spring 3.0 or earlier versions, we need to create an instance of this class to configure our properties.

package com.javapapers.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import com.javapapers.model.Resource;

@Configuration
@PropertySource({"classpath:default.properties","classpath:application.properties"})
public class RestAPIURLConfig {
    
	@Value("${restapi.url}")
    private String restAPIUrl;
	
	@Bean
    protected Resource database() {
		Resource resource = new Resource();
		resource.setUrl(restAPIUrl);
        return resource;
    }
	
	@Bean
	public static PropertySourcesPlaceholderConfigurer xxxpropertyConfig() {
		return new PropertySourcesPlaceholderConfigurer();
	}
}

Now the project structure looks like

image

Create a Junit Test Class

Spring3PropertySourceTest class configures “RestAPIURLConfig.class” as shown below to get our application configured beans to unit test them.

package com.javapapers.test;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.javapapers.config.RestAPIURLConfig;
import com.javapapers.model.Resource;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=RestAPIURLConfig.class)
public class Spring3PropertySourceTest {

		private String DEV_REST_API_URL = "http://devapp.com/restapi/results";
		private String QA_REST_API_URL = "http://qaapp.com/restapi/results";
		
		@Autowired
		private Resource resource;
	
		@Test
		//@Ignore
		public void testDevResource() {
			System.out.println("RESTful API EndPoint URL :: " + resource.getUrl());
			assertNotNull(resource);
			assertTrue(DEV_REST_API_URL.equals(resource.getUrl()));
		}
		
		@Test
		@Ignore
		public void testQAResource() {
			System.out.println("RESTful API EndPoint URL :: " + resource.getUrl());
			assertNotNull(resource);
			assertTrue(QA_REST_API_URL.equals(resource.getUrl()));

		}
}

NOTE:- It is not recommended to use System.out.println in Junit. Just for example sake it is used here ;-)

Unit Test DEV Environment

As we have commented @Ignore annotation for testDevResource() method and uncommented for testDevResource() method , we can test DEV REST API. Please run this Junit in your Java IDE and observe the results.

Console Output

image

Junit Console Output

image

By observing this screen shot, we can say that it executes testDevResource() method only and ignores testQAResource() method because of @Ignore annotation.

Spring 3.x @PropertySource Issues

If we observe Spring 3.1 @PropertySource example, we have used two property files: default.properties and application.properties. We know our main intention. If our required properties are available in application.properties, then use them in our application.If not available, then use property values from default.properties file.

For instance, application.properties file is not available at classpath, then Spring 3.x @PropertySource annotation JUnit throws the following error: java.io.FileNotFoundException: class path resource [application.properties] cannot be opened because it does not exist

It proves our assumption as false because our Junit still should work with default properties available in default.properties file. But it is throwing an error.  To see this error in our application, just delete application.properties file from “/src/main/resources” folder (Before deleting this file, please take a backup into your local file system) and run JUnits.

Spring 4.0 has provided one improvement to this annotation to avoid these errors.

@PropertySource Improvements In Spring 4.0

Spring 4.0 Framework has introduced the following improvements to @PropertySource annotation as improvement over Spring 3.x Framework features.

We will look into these Spring 4 @PropertySource annotation improvements one by one with examples.

Made @PropertySource Annotation as Java 8 Repeatable Annotation

Aside from Lambda Expressions, Stream API, Default & Static Methods in Interface, Date & Time API etc, Java SE 8 has introduced one more new feature: Repeatable Annotations.

@Target(value=TYPE)
@Retention(value=RUNTIME)
@Documented
@Repeatable(value=PropertySources.class)
public @interface PropertySource

Here @Repeatable(value=PropertySources.class) is defined for @PropertySource annotation. It indicates that it is repeatable annotation and it’s container annotation type is @PropertySources

New @PropertySources annotation in Spring 4.0

Spring 4.0 has added new @PropertySources annotation with the following definition.

@Target(value=TYPE)
@Retention(value=RUNTIME)
@Documented
public @interface PropertySources

Spring 4.0 @PropertySources annotation is used to define an array of one or more @PropertySource annotations. That’s why it is also known as a Container Annotation.

As we discussed in Spring 3.1 section, to configure more than one property file we use array as shown below:

@Configuration
@PropertySource(value={"classpath:default.properties","classpath:config.properties"})
public class RestAPIURLConfig {}

We can define same configuration using @PropertySources annotation as follows

@Configuration
@PropertySources {
@PropertySource(value={"classpath:default.properties","classpath:config.properties"})
public class RestAPIURLConfig {}

Introduced new attribute: ignoreResourceNotFound

To avoid “java.io.FileNotFoundException” exception just before seen in Spring 3.1 example, Spring 4.0 had introduced a new boolean attribute “ignoreResourceNotFound”. It accepts true or false. Default value to this attribute is false.

Example:

We will use same steps and files from “Spring3.1PropertySource” project and will discuss only few steps which are new in Spring 4.0 Project.

Spring 4.0 with Java 6 or Java 7

package com.javapapers.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.PropertySources;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import com.javapapers.model.Resource;

@Configuration
@PropertySources
({
@PropertySource(value="classpath:default.properties"),
@PropertySource(value="classpath:application.properties",ignoreResourceNotFound=true)
})
publicclass RestAPIURLConfig {

	// Same code from Spring 3.1 example
}

Spring 4.0 with Java 8

package com.javapapers.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.PropertySources;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import com.javapapers.model.Resource;

@Configuration
@PropertySource(value="classpath:default.properties"),
@PropertySource(value="classpath:application.properties",ignoreResourceNotFound=true)
publicclass RestAPIURLConfig {

	// Same code from Spring 3.1 example
}

As Java SE 8 supports Repeatable annotations, we don’t need to use @PropertySources container annotation type. We can define same @PropertySource annotation multiple times.

Copy Junit file and rename to Spring4PropertySourceTest.java

Now if we run this Junit with or without application.properties file, we will get Junit success green because @PropertySource defines ignoreResourceNotFound=true for application.properties as

@PropertySource(value="classpath:application.properties",ignoreResourceNotFound=true)

Download Example Project with Spring version 3.1Spring3.1PropertySource

Download Example Project with Spring version 4.0Spring4.0PropertySource

This Spring tutorial was added on 30/08/2015.

Comments on "Spring Properties with @PropertySource Annotation" Tutorial:

  1. lekshmanan says:

    Nice & Useful Article, Well explained about @PropertySource

  2. Ramana Reddy says:

    I am trying to specify file path as file://D:/applciation/Config/application.properties. it is saying unknownHostException D

    Is am doing any wrong here Joe?

Comments are closed for this "Spring Properties with @PropertySource Annotation" tutorial.