I introduced you to the @Value annotation to ingest the values of properties located in the properties files with the Spring framework. Of course, we can also use this @Value annotation in Spring Boot. However, in Spring Boot, we also have another annotation, @ConfigurationProperties, to do this. What is the difference between 2 annotations and why are they creating another new annotation to ingest the value of the property in the properties file? Let’s find out in this tutorial.
First, I will create a Spring Boot project as follows:
In particular, the SpringBootConfigurationPropertiesApplication class implements the CommandLineRunner interface to run the Java console, with the following content:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
package com.huongdanjava.springboot; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SpringBootConfigurationPropertiesApplication implements CommandLineRunner { public static void main(String[] args) { SpringApplication.run(SpringBootConfigurationPropertiesApplication.class, args); } @Override public void run(String... args) throws Exception { } } |
Class Student will contain student information with some attributes as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
package com.huongdanjava.springboot; public class Student { private String name; private String clazz; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getClazz() { return clazz; } public void setClazz(String clazz) { this.clazz = clazz; } } |
The student.properties file contains student information with the following content:
1 2 |
student.name=Khanh student.clazz=Class A |
Using @Value annotation, we can ingest the value of the properties in the student.properties file into the Student object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
package com.huongdanjava.springboot; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.PropertySource; import org.springframework.stereotype.Component; @Component @PropertySource("classpath:student.properties") public class Student { @Value("${student.name}") private String name; @Value("${student.clazz}") private String clazz; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getClazz() { return clazz; } public void setClazz(String clazz) { this.clazz = clazz; } } |
then display this information to the console as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
package com.huongdanjava.springboot; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SpringBootConfigurationPropertiesApplication implements CommandLineRunner { @Autowired private Student student; public static void main(String[] args) { SpringApplication.run(SpringBootConfigurationPropertiesApplication.class, args); } @Override public void run(String... args) throws Exception { System.out.println(student.getClazz() + "-" + student.getName()); } } |
Result:
Now, If you use the @ConfigurationProperties annotation in the Student class by declaring the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
package com.huongdanjava.springboot; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.PropertySource; import org.springframework.stereotype.Component; @Component @PropertySource("classpath:student.properties") @ConfigurationProperties("student") public class Student { private String name; private String clazz; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getClazz() { return clazz; } public void setClazz(String clazz) { this.clazz = clazz; } } |
running again, the results are still the same.
As you can see, with the use of the @ConfigurationProperties annotation, you do not need to declare which property of the Student object will be ingested to the value of the property in the student.properties file. Everything is completely automatic.
To do this, of course, we have to follow some rules 🙂
In the above example, “student” is the prefix that the @ConfigurationProperties annotation will use to automatically ingest properties in the student.properties file. The properties start as “student” and the rest that coincides with the name of the Student object will be ingested to that property of the Student object. Here, the rest of these properties will not distinguish between upper case, lower case, underlines or dashes.
For example, you can declare the student.properties file as follows:
1 2 |
student.N-AME=Khanh student.CLazz=Class A |
the results are still the same. This is called relaxed binding. 🙂
Of course, if our properties do not start with the student:
1 2 |
N-AME=Khanh CLazz=Class A |
then you do not need to declare the prefix in the @ConfigurationProperties annotation anymore:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
package com.huongdanjava.springboot; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.PropertySource; import org.springframework.stereotype.Component; @Component @PropertySource("classpath:student.properties") @ConfigurationProperties() public class Student { private String name; private String clazz; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getClazz() { return clazz; } public void setClazz(String clazz) { this.clazz = clazz; } } |
In case the Student object contains a collection or array, for example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
package com.huongdanjava.springboot; import java.util.List; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.PropertySource; import org.springframework.stereotype.Component; @Component @PropertySource("classpath:student.properties") @ConfigurationProperties() public class Student { private String name; private String clazz; private List<Integer> scores; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getClazz() { return clazz; } public void setClazz(String clazz) { this.clazz = clazz; } public List<Integer> getScores() { return scores; } public void setScores(List<Integer> scores) { this.scores = scores; } } |
then you can declare the following in the student.properties file to Spring Boot automatically ingest the value of the variable scores on the Student object:
1 2 3 |
N-AME=Khanh CLazz=Class A scores=7,10 |
Then the results will be:
In case the Student object contains another object, such as:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
package com.huongdanjava.springboot; import java.util.List; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.PropertySource; import org.springframework.stereotype.Component; @Component @PropertySource("classpath:student.properties") @ConfigurationProperties() public class Student { private String name; private String clazz; private List<Integer> scores; private Address address = new Address(); class Address { private String street; private String city; public String getStreet() { return street; } public void setStreet(String street) { this.street = street; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getClazz() { return clazz; } public void setClazz(String clazz) { this.clazz = clazz; } public List<Integer> getScores() { return scores; } public void setScores(List<Integer> scores) { this.scores = scores; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } } |
then with the following declaration in the properties file:
1 2 3 4 5 |
N-AME=Khanh CLazz=Class A scores=7,10 address.street=Street 1 address.city=HCM |
The information of the Address object is also ingested as normal: