Working with property files and configurations in Spring boot applications

Wageesha Erangi
9 min readFeb 20, 2021

Have you been working on Spring boot applications? You might have worked on different application development such as RESTful or SOAP and came across “application.properties” file in the “Resources” module in your Spring boot application. Some of you might ignore that file or already know what the file is for, but have never used within your application.

We will now look at some of the advantages of using property files.

Why we need to use property files inside our Spring boot Application

  • The property file is useful when we need to keep large number of values or just the application configuration values in a single file
  • We might want to run our application in different environments. The property files will let you achieve that.
  • You will be able to fetch the values from the property file, not only once but multiple times in your application where you can reuse the same property value.
  • We do not need to hard code the configuration values such as database URLs, usernames, port numbers inside the classes or modules. But we can define them in this file.
  • You might think that we have only the “application.properties” property file, as it was automatically built inside the Spring boot application when the project is initially created. We can create and maintain multiple property files within “Resources” module, where we will be able to define different types of property values in different files.
  • What if there is a need to change the database URL in your application from the current value? If it was defined inside the property file, you just have to change that. There is no need to change the existing code in order to change the configuration.
  • Even though we fetch the values from the property files to our application, we will be able to change and override the values after it was fetched.

In this article we will look at different methods of reading property files in Spring boot applications and some of the useful Spring boot annotations.

Spring boot annotations and the usage

@Bean

Inside the Spring boot application there is a Spring container which holds the important objects that has been created. These objects are called “spring beans” and they can be created with the property values or meta data that we have in our application.

When a method with a @Bean annotation is encounters within the application at the data configuration time, the method will be executed and register the return value as a bean within a Bean Factory.

@Configuration
public class ConfigDataSource {

@Bean
public DataSource dataSource() {
Datasource datasource;
..
return datasource;
}
}

@Configuration

This annotation will tell the Spring boot that a bean is initialized within a class. Then the Spring will be able to create a bean and store it in the spring container.

@Configuration
public class ConfigDataSource {
..
}

@Component

There are 2 ways to create beans using this @Component annotation within the code.

  1. Singleton
  2. Prototype

Normal classes we create inside the application are not spring beans. An example is given below.

public class Produt{
private String id;
private String name;
private int price;
..
}

When the main method is invoked, the specified spring beans will be created. However, we must specify which objects we need to create when the application is started.

Singleton object creation

Within the application, when we need an instance of a class (For example product class mentioned earlier), we have to create a new object of that class. However, using the @Component annotation we can create one object of the class and reuse it.

To specify which objects the application is required, we can use the @Component annotation with the class declaration.

@Component
public class Product {
..
}

After adding this annotation, Spring will know that we need an object of this type and will create one object which will be available in the spring container.

Here is an example of retrieving the singleton object to our code.

@SpringBootApplication
public class DemoApplication {

public static void main(String[] args) {

ConfigurableApplicationContext context =
SpringApplication.run(DemoApplication.class, args);

Product product = context.getBean(Product.class);
}
}

Additionally, when we use this get bean method within the class multiple times, the same object will be retrieved which was created at the application run time.

Prototype object creation

Although singleton object creation is happening at the application run time, the prototype object creation will not be occurred at the run time. We get to create the objects within the code at any time.

@Component
@Scope(value="prototype")
public class Product {
..
}

Furthermore, every time we invoke the get bean method, a new object will be created.

@Autowired

Autowired annotation is used to resolve the bean type and inject the dependencies or meta data for a property we have already defined.

Given that a class with some properties (For example, take the above-mentioned product class) and a constructor where the property values are defined, we can create the instance of that class manually as below.

Product product = new Product();

But in Spring boot there is a way to create the object by auto-wiring automatically.

@Component
public class Product {

private String id;
private String name;
private int price;
..
public void productDetails(){
System.out.println(name + " , " + price);
}
}

Assume we have a rest controller class. We have a product instance defined inside the Controller class and when the product endpoint is invoked, we want to call the productDetails method in the Product class.

Since we are using the @Component annotation with the product class, the product object is already created at the application run time. We need to get the object and inject it into the rest controller class product instance.

We ask spring boot using the @Autowired annotation to find the already created object from the spring container.

@RestController
public class ProductController {

@Autowired
private Product product;

@GetMapping("/product")
public String Product(){
product.productDetails();
}
..
}

Spring will search for the type of object that has been already created.

If we want to search the object by a different object name, we can define the name using the @Component annotation. To retrieve the object using the same name, the @Qualifier annotation can be used.

@Component("prod1")
public class Product {
..
}
@RestController
public class ProductController {
@Autowired
@Qualifier("prod1")
private Product product;
..
}

@ConfigurationProperties

This annotation is mainly used when we fetch the values from the property files. Spring will automatically bind the property values with the class.

@Configuration
@ConfigurationProperties
public class AppProperties{
..
}

Moreover, we can add prefixes with the key value pairs, where it defines nested values as given below.

app.name=Demo Application
app.description=Spring boot application
app.mail.port=9090
app.mail.mode=smtp

prod1.id=28001
prod1.name=ETn12
prod1.price=5200

We add the prefix value with the @ConfigurationProperties annotation. The property value injection will only happen with given prefixed key values in the property file.

In the following example, the keys with the prefix, “app” in the property file will get injected to the instances. Furthermore, the nested values such as “app.mail” will get injected to the Mail object instance defined inside the AppProperties class.

@ConfigurationProperties(prefix = "app")
public class AppProperties{
private String name;
private String description;
private Mail mail;
..
}

@Component
public class Mail{
private String port;
private String mode;
..
}

Another approach to bind the values is using this @ConfigurationProperties annotation on a @Bean method. For example, all the keys with the prefix of “prod1” will get injected to the Product instance.

@Configuration
public class AppProperties{

@Bean
@ConfigurationProperties(prefix = "prod1")
public Product product(){
return new Product();
}
}

@PropertySource

We need a way to bind the values in a property files to the variables we defined. A better way to maintain the property values is to bind the parameters to an object. We can achieve that using the @PropertySource annotation and bind the selected values to the object class.

@Configuration
@PropertySource("classpath:application.properties")
public class AppProperties{
..
}

We can also define multiple property files.

@Configuration
@PropertySource("classpath:example1.properties")
@PropertySource("classpath:example2.properties")
public class AppProperties{
..
}

or

@Configuration
@PropertySource(value = {"classpath:example1.properties",
"classpath:example1.properties"})
public class AppProperties{
..
}

@Validated

Spring provides us a way to add bean validations to the property values. We need proper validation in our application when the defined values are important.

We can keep different types of properties in the “application.properties” file as given below.

author=developer
version=3
email=example@gmail.com
hostname=localhost

The @Validation annotation is used with the Class AppProperties as given below. We can use different validations such as NotNull, Min, Max, Email or Length. The injected values will come across the validations and if they fail, the application will throw an exception at the start of the application. Furthermore, Spring provides a type safe configuration to access the property values. When fetching the values, the variable type must be matched with the value type.

@Validated
@PropertySource({"classpath:application.properties"})
public class AppProperties {

@NotNull
private String author;

@Min(value = 1)
@Max(value = 5)
private int version;

@Email
private String email;

@Length(max=10, min=1)
private String hostname;
..
}

Different methods to fetch values from a property file

Method 1 : Using @Value annotation

We can use the @Value annotation to read the property file and inject the value to our defined parameters. When using the value annotation, we tell spring to inject the value of the key we have provided.

@RestController
public class ProductController {

@Value("${prod1.name}")
private String productName;

@GetMapping("/product")
public String Product(){
return productName;
}
}

Without using the proper annotations for a example if you use the @Value annotation like in the following example, the return value would be “prod1.name”.

@Value("prod1.name")

The “$” sign and the curly brackets are required to fetch the values from the property files.

Method 2 : Using InitializingBean Interface

The next method is to fetch all the key value pairs from the property file and map them to a hash map.

In this example we are trying to fetch all the status codes, the respective descriptions and map them in a Hash Map of the type <String, String>.

Assume we have a property file (statuscode.properties) with all the status codes and the descriptions.

0=success
1=invalid id
2=invalid user name
3=invalid password
4=authentication failed
5=login failed

We have our status code reader class where we read all the values in the property file and map them as explained in the following code.

@Configuration
public class StatusCodeReader implements InitializingBean {

private static final Map<String, String> statusCodeHashMap
= new HashMap<>();

@Override
public void afterPropertiesSet() throws Exception {
ResourceBundle resourceBundle =
ResourceBundle.getBundle("statuscode");
Enumeration<String> keys = resourceBundle.getKeys();

while (keys.hasMoreElements()) {
String status = keys.nextElement();
if (!resourceBundle.containsKey(status)){
continue;
}
String description = resourceBundle.getString(status);
if (!description.isEmpty()) {
statusCodeHashMap.put(status, description);
}
}
}

// Method to retrieve the description for a given status
public static String getDescription(String statusCode){
if (statusCodeHashMap.containsKey(statusCode)) {
return statusCodeHashMap.get(statusCode);
}
return "Invalid status code";
}
}

We have made the hash map private and the static getter method to retrieve the respective error code for each status code.

String description = PropertyFileReader.getDescription("3");

Method 3 : Read external property files

All the property files mentioned above were inside the Spring boot project scope. However, when we want to fetch other properties in other directories in our computer we can use @PropertySource annotation with the full file path to that property file.

The following property file “myProperties.properties”, contains in a external directory.

author=Developer
version=1.0
configured=true

myapp.name=Spring boot test application
myapp.port=9090
myapp.build=gradle

The AppProperties class is annotated by the @PropertySource with the full file path to the property file mentioned above.

@Component
@ConfigurationProperties
@PropertySource("file:/Documents/Properties/myProperties.properties")
public class AppProperties {

private String author;
private String version;
private boolean configured;
private Myapp myapp;

public static class Myapp{

private String name;
private String port;
private String build;
..
}
..
}

Furthermore, we can fetch not only one property file, but multiple property files in different directories.

@Component
@ConfigurationProperties
@PropertySource(value = {
"file:/Documents/properties/myProperties1.properties",
"file:/Documents/properties/myProperties2.properties"
})
public class AppProperties {
..
}

Consider a scenario where both files have the same property. Which value would Spring boot fetch? Or will it throw an exception?

If the property files have the same value for the same key, the value in the last defined property file will be fetched which in this example is the “myProperties2.properties” file.

When we read from multiple files, it is not essential to have all the key values pairs in each file. Some values might be included in the first property file but not in the second file or vice versa. The Spring boot will take the union of all the key value pairs, fetch the appropriate values and overriding them in the order of the files defined with the Class declaration.

--

--

Wageesha Erangi

Computer Science & Engineering undergraduate student