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,
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.
Let us learn about configuring property source in pre Spring 3.1 project. There are mainly two approaches to configure properties.
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 }
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>
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.
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.
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>
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.
restapi.url=http://devapp.com/restapi/results
##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.
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
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 ;-)
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.
By observing this screen shot, we can say that it executes testDevResource() method only and ignores testQAResource() method because of @Ignore annotation.
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.
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.
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
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 {}
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.
ignoreResourceNotFound=true
If that resource or property file is not available in application classpath, it does not throw “java.io.FileNotFoundException” exception. It just ignores that declaration.
ignoreResourceNotFound=false
If that resource or property file is not available in application classpath, it throws “java.io.FileNotFoundException” exception. As “false” is default value to this attribute, it is similar to without using this attribute.
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-framework.version>4.0.3.RELEASE</spring-framework.version>
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 }
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
Comments are closed for "Spring Properties with @PropertySource Annotation".
Nice & Useful Article, Well explained about @PropertySource
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?