Bulk Loading of Properties in Spring Boot

Bulk Loading of Properties in Spring Boot | @Value annotation is used to read data from properties files i.e. from one key to one variable. If we have 20 variables in class then 20 times we should write @Value which is not a good approach for multiple variables. In that case, we can use Bulk loading using @ConfigurationProperties.

@ConfigurationProperties is also called ‘Bulk Loading’ or reading multiple keys from properties files to our variables at a time.

Rules while working with @ConfigurationProperties:-

  • Define the key name using the ‘prefix.variableName’ in the properties file.
  • Provide that common prefix at our class using annotation @ConfigurationProperties(prefix=””).
  • Define variables in our class including setter & getter methods. You can also override the toString() method.

In application.properties file:-

know.app.id = 5
know.app.code = knowprogram
know.app.cost = 5000
package com.knowprogram.demo;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import lombok.Getter;
import lombok.Setter;

@Component
@Getter
@Setter
@ConfigurationProperties(prefix = "know.app")
public class DataRunner implements CommandLineRunner {
    private Integer id;
    private String code;
    private Double cost;

    @Override
    public void run(String... args) throws Exception {
        System.out.println(this);
    }

    @Override
    public String toString() {
        return "DataRunner [id=" + id + ", code=" + code + ", cost=" + cost + "]";
    }

}

In Console:-

Case-1:- If the key is not present in the properties file and trying to read the same key, then what will happen?

#know.app.id = 5
know.app.code = knowprogram
know.app.cost = 5000

In the case of @Value, it is used to throw an Exception but in the case of @ConfigurationProperties, it gives the default value (null/0/0.0/false).

Internally ConfigProps uses a set method. Logic looks like:- object.setVariable(key). Example:- ob.setCode(code); // auto parsing supported

Case-2:- What if the value and datatype do not match? For example in the properties file, id contains String but the data type of the variable is int.

know.app.id = program
know.app.code = knowprogram
know.app.cost = 5000

In that case, the application will fail to start and give this message:-

Failed to bind properties under 'know.app.id' to java.lang.Integer:

Property: know.app.id
Value: "program"
Origin: class path resource [application.properties] - 1:15
Reason: failed to convert java.lang.String to java.lang.Integer (caused by java.lang.NumberFormatException: For input string: "program")

Action:
Update your application's configuration

Case-3:- What if the properties file contains duplicate values?

know.app.id = 10
know.app.id = 20
know.app.id = 30
know.app.code = knowprogram
know.app.cost = 5000

Values are read from top to bottom and the last value will be taken.

Output:-

Case-4:- What will happen if prefixes/variables do not match in bulk loading?

@ConfigurationProperties(prefix = "know.apps")
know.app.id = 10
knows.app.code = knowprogram
know.apps.cost = 5000

@ConfigurationProperties will never throw any error/exception. It will read only matching variables data. Other values are ignored, so it holds default for no-matching keys/variables.

Output:-

@ConfigurationPropties with One-Dimensional Collections and Array

If we define a variable of type List/Set/Array then in the properties file we have to pass data using the below syntax:-

prefix.variableName[index]=value

Index numbers must start from zero, and should be given in order, else application will not be started. In application.properties:-

my.app.data[0]=A
my.app.data[1]=B
my.app.data[2]=C
my.app.data[3]=C

In Class, it can be read using one of the following:-

  • List data;
  • Set data;
  • String[ ] data;

Example:-

package com.knowprogram.demo;

import java.util.List;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Component
@Setter
@Getter
@ToString
@ConfigurationProperties(prefix = "my.app")
public class CollectionDataRunner implements CommandLineRunner {

    private List<String> data;

    @Override
    public void run(String... args) throws Exception {
        System.out.println(this);
    }

}

Output:-

Case-1:- Properties with duplicate Value and Set

In application.properties:-

my.app.data[0]=A
my.app.data[1]=A
my.app.data[2]=A
my.app.data[3]=A

In Class:-

private Set<String> data;
// related Setter, Getter & toString methods

Output:-

But if we use the list or array then it will read those duplicate values also:-

private List<String> data;
// related Setter, Getter & toString methods

Output:-

Spring or Spring boot recommends us to use interface, at runtime implementation classes are auto-selected by Spring container:-

  • List -> ArrayList
  • Set -> LinkedHashSet
  • Map -> LinkedHashMap

Due to ArrayList, LinkedHashSet, and LinkedHashMap order is preserved.

package com.knowprogram.demo;

import java.util.List;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Component
@Setter
@Getter
@ToString
@ConfigurationProperties(prefix = "my.app")
public class CollectionDataRunner implements CommandLineRunner {

    private List<String> data;

    @Override
    public void run(String... args) throws Exception {
        System.out.println(data.getClass().getName());
        System.out.println(this);
    }

}

In console:-

@ConfigurationProperties with Map

Syntax:- prefix.variable.mapKey=mapVal

In application.properties file:-

my.app.subjects.eng=85
my.app.subjects.mat=95
my.app.subjects.sci=90

In Class, We must declare the Key as String only, the value can be any type:- Map<String, Integer> subjects

package com.knowprogram.demo;

import java.util.Map;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Component
@Setter
@Getter
@ToString
@ConfigurationProperties(prefix = "my.app")
public class CollectionDataRunner implements CommandLineRunner {

    private Map<String, Integer> subjects;

    @Override
    public void run(String... args) throws Exception {
        System.out.println(subjects.getClass().getName());
        System.out.println(this);
    }

}

Output:-

If duplicate keys are present then the last one will be considered. In properties file:-

my.app.subjects.eng=85
my.app.subjects.mat=95
my.app.subjects.sci=90
my.app.subjects.sci=70
my.app.subjects.sci=50

Output:-

The keys are case-sensitive and considered different. In properties file:-

my.app.subjects.eng=85
my.app.subjects.mat=95
my.app.subjects.sci=90
my.app.subjects.SCI=70

Output:-

In Java, by default read data in the String representation and later type casting will be done.

my.app.subjects.eng=85
my.app.subjects.mat=95
my.app.subjects.sci=9.0

In this case, the application failed to start. Reason: failed to convert java.lang.String to java.lang.Integer (caused by java.lang.NumberFormatException: For input string: “9.0”)

@ConfigurationProperties With Class and Object

In this case use the below syntax to define keys in properties:- prefix.hasAVariable.variable=<value>

In properties file:-

my.app.pob.pid=10
my.app.pob.pname=PEN
my.app.pob.pcost=200

Product.java:-

package com.knowprogram.demo;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class Product {
    private Integer pid;
    private String pname;
    private Double pcost; 
}

ObjectConfigPropsRunner.java:-

package com.knowprogram.demo;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Component
@Getter
@Setter
@ToString
@ConfigurationProperties(prefix = "my.app")
public class ObjectConfigPropsRunner implements CommandLineRunner {
    
    private Product pob; // HAS-A

    @Override
    public void run(String... args) throws Exception {
        System.out.println(this);
    }

}

Output:-

Example:- Perform the following tasks:-

  1. Define one CommandLine Runner.
  2. Create variables for student (sid, sname, sfee, subjects:List<String>, grade).
  3. Read data from properties files using @ConfigurationProperties.

In application.properties:-

my.app.student.sid=11
my.app.student.sname=Jerry
my.app.student.sfee=1000
my.app.student.subjects[0]=Math
my.app.student.subjects[1]=English
my.app.student.subjects[2]=Science
my.app.student.grade=A

In Student.java:-

package com.knowprogram.demo;

import java.util.List;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class Student {
    private Integer sid;
    private String sname;
    private Double sfee;
    private List<String> subjects;
    private String grade;
}

In Runner class:-

package com.knowprogram.demo;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Component
@Getter
@Setter
@ToString
@ConfigurationProperties(prefix = "my.app")
public class ObjectConfigPropsRunner implements CommandLineRunner {

    private Student student; // HAS-A

    @Override
    public void run(String... args) throws Exception {
        System.out.println(this);
    }

}

Output:-

If you enjoyed this post, share it with your friends. Do you want to share more information about the topic discussed above or do you find anything incorrect? Let us know in the comments. Thank you!

Leave a Comment

Your email address will not be published. Required fields are marked *