

Spring @ConfigurationProperties. Spring provides several ways of… | by Habeeb Okunade | Medium
Habeeb Okunade ・ ・ 10 min read
Medium
Spring provides several ways of injecting/looking up values from the application configuration properties file.
One of them is the@Value
annotation discussed in theSpring @Value annotation tricks write up.
Another one is the using@ConfigurationProperties
annotation on a configuration bean to inject properties values to a bean and use it in the class. Also is the using ofEnvironment
object to lookup values, properties or profiles in the classpath.
In this write-up, I will demonstrate how to use@ConfigurationProperties
annotation to inject properties values from theapplication.properties
orapplication.yaml
files.
Let go tohttps://start.spring.io/ to generate and bootstrap our project.
Choose Maven Project, Java as the language, give your project the group name and artefact Id. SelectSpring Web
as the only dependency for our project.
Click ‘Generate’ button to download the bootstrapped project as a zip file. Unzip the file and open it with your prefered IDE as a Maven project to download all the required dependencies and at the end of all these, your project structure should look like the image below:
Open the application.properties file and write the properties values below:
#DEV environment propertiesdev.name=Development Applicationdev.port=8091dev.dbtype=Maria DBdev.version=1.0.alphadev.dburl=jdbc:mariadb://localhost:3306/dev.dbname=studentDBdev.dbuser=rootdev.dbpassword=root#QA environment propertiesqa.name=QA Test Applicationqa.port=8092qa.dbtype=Mongo DBqa.version=1.2.betaqa.dburl=mongodb://mongodb0.example.com:27017/qa.dbname=studentDBqa.dbuser=adminqa.dbpassword=admin#PROD environment propertiesprod.name=Production Applicationprod.port=8091prod.dbtype=MySQLprod.version=1.0.1prod.dburl=jdbc:mysql://localhost:3308/prod.dbname=studentDBprod.dbuser=admin-prodprod.dbpassword=admin-prod
The file contains three properties values for three different environment — dev, qa and prod. Each property is prefixed with the type of environment they belong to. This is not a good practice to have all these specific environment properties on a single file. In reality, we cannot have different environment properties like this, but rather create a specific application properties file for each environment, set up aprofile
and choose the active profile or profiles to use. Just bear with me with this approach to show how to use the annotation. Our aim is to load these properties into our application by creating beans and using@ConfigurationProperties
annotation.
Let’s create a package called config and create three classesDevConfigProps.java, QaConfigProps.java andProdConfigProps.java
in this package and annotate each class with@Configuration
to tell spring that these classes should be scanned, managed and configured as spring beans during application bootup.
If you consider theapplication.properties
file above, you will notice that each of these properties is either prefix withdev
,qa
orprod
. These prefixes will be used to configure these classes by using the@ConfigurationProperties("prefix")
annotation.
@ConfigurationProperties("dev")
for dev properties,@ConfigurationProperties("qa")
for qa properties and@ConfigurationProperties("prod")
for prod properties.
//DevConfigProps.javapackagecom.habeebcycle.configurationpropertiesannotation.config;importorg.springframework.boot.context.properties.ConfigurationProperties;importorg.springframework.context.annotation.Configuration;@Configuration@ConfigurationProperties("dev")publicclassDevConfigProps{}//QaConfigProps.javapackagecom.habeebcycle.configurationpropertiesannotation.config;importorg.springframework.boot.context.properties.ConfigurationProperties;importorg.springframework.context.annotation.Configuration;@Configuration@ConfigurationProperties("qa")publicclassQaConfigProps{}//ProdConfigProps.javapackagecom.habeebcycle.configurationpropertiesannotation.config;importorg.springframework.boot.context.properties.ConfigurationProperties;importorg.springframework.context.annotation.Configuration;@Configuration@ConfigurationProperties("prod")publicclassProdConfigProps{}
@ConfigurationProperties
allows us to map the entire properties or YAML files into an object easily. It also allows us to validate properties with JSR-303 bean validation. By default, the annotation reads from theapplication.properties
file. The properties file to be used can be changed with@PropertySource("file-name")
annotation if we don’t want to use the default properties file.
The next step is to create all the properties names as a variable with their setters and getters.
forDevConfigProps.java
packagecom.habeebcycle.configurationpropertiesannotation.config;importorg.springframework.boot.context.properties.ConfigurationProperties;importorg.springframework.context.annotation.Configuration;@Configuration@ConfigurationProperties("dev")publicclassDevConfigProps{privateStringname;privateintport;privateStringdbType;privateStringversion;privateStringdbUrl;privateStringdbName;privateStringdbUser;privateStringdbPassword;publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicintgetPort(){returnport;}publicvoidsetPort(intport){this.port=port;}publicStringgetDbType(){returndbType;}publicvoidsetDbType(StringdbType){this.dbType=dbType;}publicStringgetVersion(){returnversion;}publicvoidsetVersion(Stringversion){this.version=version;}publicStringgetDbUrl(){returndbUrl;}publicvoidsetDbUrl(StringdbUrl){this.dbUrl=dbUrl;}publicStringgetDbName(){returndbName;}publicvoidsetDbName(StringdbName){this.dbName=dbName;}publicStringgetDbUser(){returndbUser;}publicvoidsetDbUser(StringdbUser){this.dbUser=dbUser;}publicStringgetDbPassword(){returndbPassword;}publicvoidsetDbPassword(StringdbPassword){this.dbPassword=dbPassword;}}
forQaConfigProps.java
packagecom.habeebcycle.configurationpropertiesannotation.config;importorg.springframework.boot.context.properties.ConfigurationProperties;importorg.springframework.context.annotation.Configuration;@Configuration@ConfigurationProperties("qa")publicclassQaConfigProps{privateStringname;privateintport;privateStringdbType;privateStringversion;privateStringdbUrl;privateStringdbName;privateStringdbUser;privateStringdbPassword;publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicintgetPort(){returnport;}publicvoidsetPort(intport){this.port=port;}publicStringgetDbType(){returndbType;}publicvoidsetDbType(StringdbType){this.dbType=dbType;}publicStringgetVersion(){returnversion;}publicvoidsetVersion(Stringversion){this.version=version;}publicStringgetDbUrl(){returndbUrl;}publicvoidsetDbUrl(StringdbUrl){this.dbUrl=dbUrl;}publicStringgetDbName(){returndbName;}publicvoidsetDbName(StringdbName){this.dbName=dbName;}publicStringgetDbUser(){returndbUser;}publicvoidsetDbUser(StringdbUser){this.dbUser=dbUser;}publicStringgetDbPassword(){returndbPassword;}publicvoidsetDbPassword(StringdbPassword){this.dbPassword=dbPassword;}}
forProdConfigProps.java
packagecom.habeebcycle.configurationpropertiesannotation.config;importorg.springframework.boot.context.properties.ConfigurationProperties;importorg.springframework.context.annotation.Configuration;@Configuration@ConfigurationProperties("prod")publicclassProdConfigProps{privateStringname;privateintport;privateStringdbType;privateStringversion;privateStringdbUrl;privateStringdbName;privateStringdbUser;privateStringdbPassword;publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicintgetPort(){returnport;}publicvoidsetPort(intport){this.port=port;}publicStringgetDbType(){returndbType;}publicvoidsetDbType(StringdbType){this.dbType=dbType;}publicStringgetVersion(){returnversion;}publicvoidsetVersion(Stringversion){this.version=version;}publicStringgetDbUrl(){returndbUrl;}publicvoidsetDbUrl(StringdbUrl){this.dbUrl=dbUrl;}publicStringgetDbName(){returndbName;}publicvoidsetDbName(StringdbName){this.dbName=dbName;}publicStringgetDbUser(){returndbUser;}publicvoidsetDbUser(StringdbUser){this.dbUser=dbUser;}publicStringgetDbPassword(){returndbPassword;}publicvoidsetDbPassword(StringdbPassword){this.dbPassword=dbPassword;}}
To test our properties, let’s create a configuration class that implementsCommandLineRunner
so that we can log all the properties configured in itsrun
method. In this class, we will have these three configuration classes autowired since they are spring managed beans.
packagecom.habeebcycle.configurationpropertiesannotation;importcom.habeebcycle.configurationpropertiesannotation.config.DevConfigProps;importcom.habeebcycle.configurationpropertiesannotation.config.ProdConfigProps;importcom.habeebcycle.configurationpropertiesannotation.config.QaConfigProps;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.boot.CommandLineRunner;importorg.springframework.context.annotation.Configuration;@ConfigurationpublicclassPropertiesConfigimplementsCommandLineRunner{privatefinalDevConfigPropsdevConfigProps;privatefinalQaConfigPropsqaConfigProps;privatefinalProdConfigPropsprodConfigProps;privatestaticfinalLoggerlogger=LoggerFactory.getLogger(ConfigurationPropertiesAnnotationApplication.class);@AutowiredpublicPropertiesConfig(DevConfigPropsdevConfigProps,QaConfigPropsqaConfigProps,ProdConfigPropsprodConfigProps){this.devConfigProps=devConfigProps;this.qaConfigProps=qaConfigProps;this.prodConfigProps=prodConfigProps;}@Overridepublicvoidrun(String...args)throwsException{logger.info("-------------Dev Properties-----------------");logger.info("Name: {}",devConfigProps.getName());logger.info("Port: {}",devConfigProps.getPort());logger.info("DB Type: {}",devConfigProps.getDbType());logger.info("Version: {}",devConfigProps.getVersion());logger.info("DB Url: {}",devConfigProps.getDbUrl());logger.info("DB name: {}",devConfigProps.getDbName());logger.info("DB User: {}",devConfigProps.getDbUser());logger.info("DB Password: {}",devConfigProps.getDbPassword());logger.info("-------------QA Properties-----------------");logger.info("Name: {}",qaConfigProps.getName());logger.info("Port: {}",qaConfigProps.getPort());logger.info("DB Type: {}",qaConfigProps.getDbType());logger.info("Version: {}",qaConfigProps.getVersion());logger.info("DB Url: {}",qaConfigProps.getDbUrl());logger.info("DB name: {}",qaConfigProps.getDbName());logger.info("DB User: {}",qaConfigProps.getDbUser());logger.info("DB Password: {}",qaConfigProps.getDbPassword());logger.info("-------------Prod Properties---------------");logger.info("Name: {}",prodConfigProps.getName());logger.info("Port: {}",prodConfigProps.getPort());logger.info("DB Type: {}",prodConfigProps.getDbType());logger.info("Version: {}",prodConfigProps.getVersion());logger.info("DB Url: {}",prodConfigProps.getDbUrl());logger.info("DB name: {}",prodConfigProps.getDbName());logger.info("DB User: {}",prodConfigProps.getDbUser());logger.info("DB Password: {}",prodConfigProps.getDbPassword());}}
Running the application as a SpringBoot application will log the following into the console:
---------------------Dev Properties-------------------------2020-03-01 17:26:25.625 [main]: Name: Development Application2020-03-01 17:26:25.626 [main]: Port: 80912020-03-01 17:26:25.626 [main]: DB Type: Maria DB2020-03-01 17:26:25.626 [main]: Version: 1.0.alpha2020-03-01 17:26:25.626 [main]: DB Url: jdbc:mariadb://localhost:3306/2020-03-01 17:26:25.626 [main]: DB name: studentDB2020-03-01 17:26:25.626 [main]: DB User: root2020-03-01 17:26:25.627 [main]: DB Password: root---------------------QA Properties-------------------------2020-03-01 17:26:25.627 [main]: Name: QA Test Application2020-03-01 17:26:25.627 [main]: Port: 80922020-03-01 17:26:25.627 [main]: DB Type: Mongo DB2020-03-01 17:26:25.627 [main]: Version: 1.2.beta2020-03-01 17:26:25.627 [main]: DB Url: mongodb://mongodb0.example.com:27017/2020-03-01 17:26:25.627 [main]: DB name: studentDB2020-03-01 17:26:25.627 [main]: DB User: admin2020-03-01 17:26:25.627 [main]: DB Password: admin---------------------Prod Properties-------------------------2020-03-01 17:26:25.627 [main]: Name: Production Application2020-03-01 17:26:25.627 [main]: Port: 80912020-03-01 17:26:25.627 [main]: DB Type: MySQL2020-03-01 17:26:25.628 [main]: Version: 1.0.12020-03-01 17:26:25.628 [main]: DB Url: jdbc:mysql://localhost:3308/2020-03-01 17:26:25.628 [main]: DB name: studentDB2020-03-01 17:26:25.628 [main]: DB User: admin-prod2020-03-01 17:26:25.628 [main]: DB Password: admin-prod
Using@ConfigurationProperties
to inject List properties.
We can also use@ConfigurationProperties
to read List values from a properties file.
#application.propertiesdev.mylist=SUN,MON,TUE,WED,THU,FRI,SAT
and use@ConfigurationProperties
to inject the list as follows
packagecom.habeebcycle.configurationpropertiesannotation.config;importorg.springframework.boot.context.properties.ConfigurationProperties;importorg.springframework.context.annotation.Configuration;importjava.util.List;@Configuration@ConfigurationProperties("dev")publicclassDevConfigProps{privateList<String>myList;publicList<String>getMyList(){returnmyList;}publicvoidsetMyList(List<String>myList){this.myList=myList;}}
Logging the property gives
[SUN, MON, TUE, WED, THU, FRI, SAT]
Using@ConfigurationProperties
to inject Map (key-value pairs) properties
key-value pair properties can be read or injected with@ConfigurationProperties
annotation as follows:
#application.propertiesdev.db.url=jdbc:mysql://localhost:3308/dev.db.name=student_marksdev.db.user=rootdev.db.password=root
From the properties file above, we can see that after the prefixdev
, we havedb
. Thisdb
will be the variable to use to inject the values as a key-value map.
packagecom.habeebcycle.configurationpropertiesannotation.config;importorg.springframework.boot.context.properties.ConfigurationProperties;importorg.springframework.context.annotation.Configuration;importjava.util.Map;@Configuration@ConfigurationProperties("dev")publicclassDevConfigProps{privateMap<String,String>db;publicMap<String,String>getDb(){returndb;}publicvoidsetDb(Map<String,String>db){this.db=db;}}
Spring will automatically inject the nested properties into thedb
variables as Map variable.
Logging out the property value ofdevConfigProps.getDb()
, will output the following:
{"url":"jdbc:mysql://localhost:3308/","name":"student_marks","user":"root","password":"root"}
Using@ConfigurationProperties
to inject a class (Object) from a properties file
Let’s create a class calledStudent.java
packagecom.habeebcycle.configurationpropertiesannotation.model;publicclassStudent{privateStringid;privateStringname;privateintage;privateStringlevel;privatedoublemark;publicStringgetId(){returnid;}publicvoidsetId(Stringid){this.id=id;}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicintgetAge(){returnage;}publicvoidsetAge(intage){this.age=age;}publicStringgetLevel(){returnlevel;}publicvoidsetLevel(Stringlevel){this.level=level;}publicdoublegetMark(){returnmark;}publicvoidsetMark(doublemark){this.mark=mark;}}
If we have a properties file as follows
#application.propertiesdev.student.id=ABC123XYZdev.student.name=Habeeb Okunadedev.student.age=34dev.student.level=700Jdev.student.mark=99.4
From the properties file above, we can see that after the prefixdev, we have
. Thisstudent*
student
will be the variable to use to inject the values as aStudent*
object.
packagecom.habeebcycle.configurationpropertiesannotation.config;importcom.habeebcycle.configurationpropertiesannotation.model.Student;importorg.springframework.boot.context.properties.ConfigurationProperties;importorg.springframework.context.annotation.Configuration;@Configuration@ConfigurationProperties("dev")publicclassDevConfigProps{privateStudentstudent;publicStudentgetStudent(){returnstudent;}publicvoidsetStudent(Studentstudent){this.student=student;}}
Spring will automatically inject the nested properties into thestudent
object as Student variable.
Logging out the property value ofdevConfigProps.getStudent()
, will output the following:
{"id":"ABC123XYZ","name":"Habeeb Okunade","age":34,"level":"700J","mark":99.4}
Using@ConfigurationProperties
on a@Bean
method
We can also use@ConfigurationProperties
annotation on@Bean
-annotated methods.
This approach may be particularly useful when we want to bind properties to a third-party component that’s outside of our control.
Let’s create a simpleSchool
class to show how to use this:
packagecom.habeebcycle.configurationpropertiesannotation.model;publicclassSchool{privateStringid;privateStringname;privateintsize;publicStringgetId(){returnid;}publicvoidsetId(Stringid){this.id=id;}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicintgetSize(){returnsize;}publicvoidsetSize(intsize){this.size=size;}}
Now, let’s see how we can use@ConfigurationProperties
on a@Bean
method to bind externalized properties to theSchool
instance:
packagecom.habeebcycle.configurationpropertiesannotation.config;@ConfigurationpublicclassExternalConfigProperties{@Bean@ConfigurationProperties("school")publicSchoolschool(){returnnewSchool();}}
By this, anyschool
-prefixed property will be mapped to the**School
instance managed by the Spring context.
Using@ConfigurationProperties
for property validation
@ConfigurationProperties
provides validation of properties using the JSR-303 format. This allows our properties to come out neatly. Consider the following class:
packagecom.habeebcycle.configurationpropertiesannotation.config;importorg.hibernate.validator.constraints.Length;importorg.springframework.boot.context.properties.ConfigurationProperties;importorg.springframework.context.annotation.Configuration;importorg.springframework.validation.annotation.Validated;importjavax.validation.constraints.Max;importjavax.validation.constraints.Min;importjavax.validation.constraints.NotNull;importjavax.validation.constraints.Pattern;@Configuration@ConfigurationProperties("validate")@ValidatedpublicclassConfigValidation{@NotNullprivateStringdbUrl;@Length(min=4,max=10)privateStringdbUser;@Min(8080)@Max(8100)privateintdbPort;@Pattern(regexp="^[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,6}$")privateStringadminEmail;publicStringgetDbUrl(){returndbUrl;}publicvoidsetDbUrl(StringdbUrl){this.dbUrl=dbUrl;}publicStringgetDbUser(){returndbUser;}publicvoidsetDbUser(StringdbUser){this.dbUser=dbUser;}publicintgetDbPort(){returndbPort;}publicvoidsetDbPort(intdbPort){this.dbPort=dbPort;}publicStringgetAdminEmail(){returnadminEmail;}publicvoidsetAdminEmail(StringadminEmail){this.adminEmail=adminEmail;}}
This class has some validation rules for the@ConfigurationProperties
annotation to work.
The@NotNull
annotation tells the Spring that thedbUrl
field is mandatory and throws an error if not found.
The@Length(min=4, max=6)
annotation validatedbUser
to only inject property of a minimum of 4 characters and 6 characters long.
The@Min
and@Max
annotation to injectport
value in the range of 8080 to 8100.
lastly, theadminEmail
property must match the pattern provided as@Pattern(regexp = "^[a-z0–9._%+-]+@[a-z0–9.-]+\.[a-z]{2,6}$")
This helps us reduce a lot of if-else conditions in our code and makes it look much cleaner and concise.
If any of these validations fail, then the main application would fail to start with anIllegalStateException
.
In this write-up, I have shown how to use@ConfigurationProperties
to read /inject configuration properties from a properties file and explained some of the handy features it provides like property mapping, binding and validation.
Top comments(3)

- LocationGermany
- WorkJunior Backend Developer at SW Machines
- Joined
Thanks for the post.
What I like most about the feature is the possibility to avoidsetters()
using@ConstructorBinding
, making the properties from a YAMLprivate final
and therefore immutable. And this goes hand in hand with Lombok's@AllArgsConstructor
and@Getter
.
For example:
@Getter@Validated@AllArgsConstructor@ConstructorBinding@ConfigurationProperties("foo")publicclassFooBarProperties{@NotBlankprivatefinalStringbar;}

- LocationUSA
- WorkSenior Software Engineer at Logistics Company
- Joined
This makes sense from a perspective of each environment having its own configuration but this isn’t what you actually want in the end. You want the configuration to be driven by the environment so that the code is completely environment agnostic. In this example, how would you use the QA config specifically in the QA environment?

- LocationSydney
- WorkSoftware Engineer at Scopesuite
- Joined
Hi Ben,
This article is not meant to explain how to configure properties environment but rather on how to use @ConfigurationProperties annotation. There are so many technologies out there to use in QA & Prod environments like docker config/env file, K8s configMap/Secrets, Hashicorp vault, Spring config server etc.
I will try to write something about it in the next article. This one is just to explain the usefulness of @ConfigurationProperties annotation in a simple spring application.
For further actions, you may consider blocking this person and/orreporting abuse