Reading External Configuration Properties in Spring
2 CommentsLast Updated on February 3, 2021 by jt
Enterprise Applications developed using the Spring Framework use different types of configuration properties to configure the application at runtime. These configuration properties help in connecting to databases, messaging systems, perform logging, caching, and lots more.
It is common to store configuration properties in external .properties and .yml files. There are various ways of reading external configuration properties in Spring.
In this post, we will see how to read external properties using annotations, such as @PropertySource, @Environment, @Value, and @ConfigurationProperties.
Reading as Property Value
In this post, I will use a Spring Boot application that performs operations on a Blog entity. Also, I will use Lombok to generate code for the Blog entity.
The code of the Blog entity is this.
Blog.java
package guru.springframework.domain;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Blog {
@Id
private String blogId;
private String blogTitle;
private String authorName;
private String blogDescription;
}
The application.yml file containing configuration properties of the application is this.
server:
port: 8088
guru:
springframework:
blog:
id: 202
title: The Huffington Post
author: Arianna
description: The history of political blogging might usefully be divided into the periods pre- and post-Huffington.
toptitles: The Huffington Post, Solo Traveller, Meghna's Diary
topbloggers: Meghna N;Arianna;Paul Grater;
topblogs: {"The Huffington Post":"Arianna","Meghnas Diary":"Meghna N","Solo Traveller":"Paul Grater"}
Next, I will define a configuration class to access the properties defined in the preceding code. I will also define a bean to create a new blog.
The code for ExternalPropertyValueDemo.java is this.
ExternalPropertyValueDemo.java
package guru.springframework.demo;
import guru.springframework.domain.Blog;
import lombok.Data;
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 java.util.List;
@Data
@Configuration
@PropertySource("classpath:application.yml")
public class ExternalPropertyValueDemo {
@Value("${guru.springframework.blog.id}")
private String blogId;
@Value("${guru.springframework.blog.title}")
private String blogTitle;
@Value("${guru.springframework.blog.author}")
private String authorName;
@Value("${guru.springframework.blog.description}")
private String description;
@Value("${guru.springframework.toptitles}")
private String[] topTitles;
@Value("#{'${guru.springframework.topbloggers}'.split(';')}")
private List<String> topBloggers;
@Bean("simpleBlog")
public Blog createBlog() {
Blog blog = new Blog(blogId, blogTitle, authorName, description);
System.out.println(blog);
return blog;
}
}
The preceding code uses the @PropertySource annotation to specify an application.yml file to load from the classpath. The code then injects the values of the properties of application.yml in the class fields using the @Value annotation.
The code also creates a Blog bean named simpleBlog initialized with the properties read from the application.yml file.
To test this class, I will write a JUnit 5 test.
The test class, ExternalPropertyValueDemoTest is this.
ExternalPropertyValueDemoTest.java
package guru.springframework.demo;
import guru.springframework.domain.Blog;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest
class ExternalPropertyValueDemoTest {
@Autowired
@Qualifier("simpleBlog")
private Blog simpleBlog;
@Autowired
ExternalPropertyValueDemo externalPropertyValueDemo;
private String[] expectedTopTitles;
private List<String> expectedTopBloggersList;
private Map<String, String> expectedTopBlogs;
@BeforeEach
public void setUp() {
expectedTopTitles = new String[]{"The Huffington Post", "Solo Traveller", "Meghna's Diary"};
expectedTopBloggersList = new ArrayList<String>() {
{
add("Meghna N");
add("Arianna");
add("Paul Grater");
} };
expectedTopBlogs = new HashMap<String, String>() {
{
put("The Huffington Post", "Arianna");
put("Meghna's Diary", "Meghna N");
put("Solo Traveller", "Paul Grater");
}
};
}
@AfterEach
public void tearDown() {
expectedTopTitles = null;
expectedTopBloggersList = null;
expectedTopBlogs = null;
}
@Test
public void testExternalPropertyReadwithValue() {
assertThat(simpleBlog.getBlogId()).isEqualTo("202");
assertThat(simpleBlog.getBlogTitle()).isEqualTo("The Huffington Post");
assertThat(simpleBlog.getAuthorName()).isEqualTo("Arianna");
}
@Test
public void testExternalPropertyReadForArray() {
assertThat(externalPropertyValueDemo.getTopTitles()).containsExactly("The Huffington Post", "Solo Traveller", "Meghna's Diary");
}
@Test
public void testExternalPropertyReadForList() {
assertThat(externalPropertyValueDemo.getTopBloggers()).containsExactly("Meghna N", "Arianna", "Paul Grater");
}
}
Reading as Environment Properties
Spring comes with the Environment interface that represents the environment in which the current application is running. We can read configuration properties using this Environment. I will define a configuration class for that.
The code for ExternalPropertyEnvironmentDemo class is this.
ExternalPropertyEnvironmentDemo.java
package guru.springframework.demo;
import guru.springframework.domain.Blog;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import java.util.Arrays;
import java.util.List;
@Data
@NoArgsConstructor
@Configuration
@PropertySource(value = "classpath:application.yml", ignoreResourceNotFound = true)
//Class to Demonstrate @Environment
public class ExternalPropertyEnvironmentDemo {
/**
* Environment to get the property values
*/
private Environment environment;
private String[] topTitles;
private List<String> topBloggers;
@Autowired
public void ExternalPropertyEnvironmentDemo(Environment environment) {
this.environment = environment;
this.setTopTitles();
this.setTopBloggers();
}
@Bean("environmentBlog")
public Blog createBlog() {
Blog blog = new Blog(environment.getProperty("id"), environment.getProperty("title"), environment.getProperty("author"), environment.getProperty("description"));
return blog;
}
public void setTopTitles() {
this.topTitles = environment.getProperty("toptitles", String[].class);
}
public void setTopBloggers() {
this.topBloggers = Arrays.asList(environment.getProperty("topbloggers", "").split(";"));
}
}
In the preceding code, I have used ignoreResourceNotFound=true attribute in @PropertySource annotation to avoid java.io.FileNotFoundException .
Next, the code autowires in the Environment. Then, the code calls the environment.getProperty() method to get the property value from the application.yml file.
The code to test the above class is this.
ExternalPropertyEnvironmentDemoTest.java
package guru.springframework.demo;
import guru.springframework.domain.Blog;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest
class ExternalPropertyEnvironmentDemoTest {
@Autowired
@Qualifier("environmentBlog")
private Blog environmentBlog;
@Autowired
private ExternalPropertyEnvironmentDemo externalPropertyEnvironmentDemo;
private String[] expectedTopTitles;
private List<String> expectedTopBloggersList;
private Map<String, String> expectedTopBlogs;
@BeforeEach
public void setUp() {
expectedTopTitles = new String[]{"The Huffington Post", "Solo Traveller", "Meghna's Diary"};
expectedTopBloggersList = new ArrayList<String>() {
{
add("Meghna N");
add("Arianna");
add("Paul Grater");
} };
expectedTopBlogs = new HashMap<String, String>() {
{
put("The Huffington Post", "Arianna");
put("Meghna's Diary", "Meghna N");
put("Solo Traveller", "Paul Grater");
}
};
}
@AfterEach
public void tearDown() {
expectedTopTitles = null;
expectedTopBloggersList = null;
expectedTopBlogs = null;
}
@Test
public void testExternalPropertyReadwithValue() {
assertThat(environmentBlog.getBlogId()).isEqualTo("202");
assertThat(environmentBlog.getBlogTitle()).isEqualTo("The Huffington Post");
assertThat(environmentBlog.getAuthorName()).isEqualTo("Arianna");
}
@Test
public void testExternalPropertyReadForArray() {
assertThat(externalPropertyEnvironmentDemo.getTopTitles()).containsExactly("The Huffington Post", "Solo Traveller", "Meghna's Diary");
}
@Test
public void testExternalPropertyReadForList() {
assertThat(externalPropertyEnvironmentDemo.getTopBloggers()).containsExactly("Meghna N", "Arianna", "Paul Grater");
}
}
Multiple Properties File
Next, I will show you how to fetch properties from multiple property files.
For this, I will use @PropertySources annotation that contains the classpath for multiple properties file.
I will create two properties file.
The code for demoa.properties is this.
demoa.properties
guru.springframework.blog.id= 202 guru.springframework.blog.title= The Huffington Post guru.springframework.blog.author= Arianna guru.springframework.blog.description= The history of political blogging might usefully be divided into the periods pre- and post-Huffington.
The code for demob.properties is this.
demob.properties
guru.springframework.microblog.blogger= JT guru.springframework.microblog.blogid= 12 guru.springframework.microblog.content= Spring Boot releases version 3
Next, I will add a configuration class, MultiplePropertySourceDemo to access properties from both the demoa.properties and demob.properties files.
The code of the MultiplePropertySourceDemo class is this.
MultiplePropertySourceDemo.java
package guru.springframework.demo;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.PropertySources;
import org.springframework.core.env.Environment;
@Data
@Configuration
@PropertySources({
@PropertySource("classpath:demoa.properties"),
@PropertySource("classpath:demob.properties")
})
public class MultiplePropertySourceDemo {
//Fields to read from demoa.properties
private String blogId;
private String blogTitle;
//Fields to read from demob.properties
private String microBlogId;
private String content;
@Autowired
public MultiplePropertySourceDemo(Environment environment) {
this.blogId = environment.getProperty("guru.springframework.blog.id");
this.blogTitle = environment.getProperty("guru.springframework.blog.title");
this.microBlogId = environment.getProperty("guru.springframework.microblog.blogid");
this.content = environment.getProperty("guru.springframework.microblog.content");
}
}
In the preceding code, I have accessed the values from demoa.properties and demob.properties using the environment.getProperty() method.
The code to test the above class is this.
MultiplePropertySourceDemoTest.java
package guru.springframework.demo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest
class MultiplePropertySourceDemoTest {
@Autowired
MultiplePropertySourceDemo multiplePropertySourceDemo;
@Test
public void testForBlogId() {
assertThat(multiplePropertySourceDemo.getBlogId()).isEqualTo("202");
}
@Test
public void testForBlogTitle() {
assertThat(multiplePropertySourceDemo.getBlogTitle()).isEqualTo("The Huffington Post");
}
@Test
public void testForMicroBlogId() {
assertThat(multiplePropertySourceDemo.getMicroBlogId()).isEqualTo("12");
}
@Test
public void testForMicroBlogContent() {
assertThat(multiplePropertySourceDemo.getContent()).isEqualTo("Spring Boot releases version 3");
}
}
Using @ConfigurationProperties
Next, we will see how to map the entire Properties or YAML files to an object. For this, I will use @ConfigurationProperties annotation.
I will add a dependency to generate metadata for the classes annotated with @ConfigurationProperties.
The dependency code in pom.xml is this.
pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>
I will access values from the demob.properties file.
I will create a MicroBlog domain class to map the properties to.
The code for MicroBlog is this.
MicroBlog.java
package guru.springframework.domain;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@Configuration("microBlog")
@PropertySource("classpath:demob.properties")
@Data
@NoArgsConstructor
@ConfigurationProperties(prefix="guru.springframework.microblog")
public class MicroBlog {
private String blogid;
private String content;
private String blogger;
}
In the above code, I have defined the classpath as demob.properties in the @PropertySource annotation. Next, I have used the @ConfigurationProperties annotation to set the prefix for the properties to fetch.
I will define the MicroBlogclass as a bean ins configuration class
The code of the configuration class, ConfigurationPropertiesDemo class is this.
ConfigurationPropertiesDemo.java
package guru.springframework.demo;
import guru.springframework.domain.MicroBlog;
import org.springframework.context.annotation.Bean;
public class ConfigurationPropertiesDemo {
@Bean("microBlog")
public MicroBlog createBlog() {
return new MicroBlog();
}
}
The code to test the above class is this.
package guru.springframework.demo;
import guru.springframework.domain.MicroBlog;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest
class ConfigurationPropertiesDemoTest {
@Autowired
MicroBlog microBlog;
@Test
public void testConfigurationProperty() {
assertThat(microBlog.getBlogid()).isEqualTo("12");
assertThat(microBlog.getBlogger()).isEqualTo("JT");
assertThat(microBlog.getContent()).isEqualTo("Spring Boot releases version 3");
}
}
Profile-based Configuration Settings
Let’s now see how to fetch properties based on profiles. For this, I will use spring.profiles.active to set the active profile.
I will use two property files application-dev.yml that contains embedded h2 console configuration properties and application-prod.yml containing MySQL configuration properties.
I will add dependencies for MySQL, embedded H2, and Spring Data JPA.
The dependency code in pom.xml is this.
pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> <version>1.4.199</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.13</version> </dependency>
I will add configuration properties in the YAML file.
The code for application-dev.yml is this.
application-dev.yml
spring:
h2:
console:
enabled: true
path: /h2-console
datasource:
username: sa
password:
url: jdbc:h2:mem:testdb
driverClassName: org.h2.Driver
database-platform: org.hibernate.dialect.H2Dialect
The code for application-prod.yml is this.
application-prod.yml
spring:
jpa:
hibernate:
ddlAuto: update
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL5Dialect
datasource:
username: root
password: root
url: jdbc:mysql://localhost:3306/blogDb?useSSL=false&createDatabaseIfNotExist=true&allowPublicKeyRetrieval=true
driverClassName: com.mysql.jdbc.DriverNext, I will set the active profile in application.yml.
spring: profiles: active: prod
In the preceding code, I have set prod as the active profile. As a result, Spring will pick the properties present in the application-prod.yml file at runtime.
Now I will create a class to load data when the application starts.
package guru.springframework.bootstrap;
import guru.springframework.domain.Blog;
import guru.springframework.repository.BlogRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
@Component
public class BootstrapData implements ApplicationListener<ContextRefreshedEvent> {
private BlogRepository blogRepository;
@Autowired
private BootstrapData(BlogRepository blogRepository) {
this.blogRepository = blogRepository;
}
public BootstrapData() {
}
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
Blog blog = Blog.builder()
.blogId("201")
.blogTitle("One Mile At A Time")
.blogDescription("The latest travel news, reviews, and strategies to maximize elite travel status")
.authorName("Mat Tree")
.build();
try {
blogRepository.save(blog);
} catch (Exception exception) {
exception.printStackTrace();
}
}
}
In the preceding code, the BootstrapData class implements ApplicationListener interface. In onApplicationEvent() method, I haveWe will pre-filled the database whenever the application starts.
When I run the application, I get the following as output.
The following profiles are active: prod
Summary
I often see @Value being used to read configuration properties. I’m not a particular fan of this approach – particularly in enterprise applications. This is because you end up with scattered configuration code (@Value) across your application classes.
Now, what happens if a property name changes?
You end up trying to find out all affected @Value code and updating them with the new property name.
Instead, you should encapsulate configuration as a service of your application. If you do so, you will have a single point of responsibility to load and get your configuration from.
By using @ConfigurationProperties you can easily encapsulate your configuration in a separate class.

DivyaGanesh
Thanks you share the best software technologies for freshers and experience candidates to upgrade the next level in an Software Industries Technologies.
Prasanna
In case of spring mvc application can we achieve hot reloading (without restarting server instance can we get updated properties).
if yes how can we achieve that?