Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for Spring @ConfigurationProperties annotation explained
Habeeb Okunade
Habeeb Okunade

Posted on

     

Spring @ConfigurationProperties annotation explained

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 havestudent*. Thisstudent 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)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
pixelstuermer profile image
Philipp Montesano
☕️ Life starts after coffee
  • Location
    Germany
  • Work
    Junior Backend Developer at SW Machines
  • Joined
• Edited on• Edited

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;}
Enter fullscreen modeExit fullscreen mode
CollapseExpand
 
brogrammerben profile image
Ben
Software engineer with a strong passion for learning and teaching
  • Location
    USA
  • Work
    Senior Software Engineer at Logistics Company
  • Joined
• Edited on• Edited

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?

CollapseExpand
 
habeebcycle profile image
Habeeb Okunade
Loves sharing knowledge and learning the latest tech stacks. A full-stack engineer expertise in microservices architecture using Java, Node, PHP, Salesforce, ReactJS - GCP, Azure and AWS stacks.
  • Location
    Sydney
  • Work
    Software 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.

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Loves sharing knowledge and learning the latest tech stacks. A full-stack engineer expertise in microservices architecture using Java, Node, PHP, Salesforce, ReactJS - GCP, Azure and AWS stacks.
  • Location
    Sydney
  • Work
    Software Engineer at Scopesuite
  • Joined

More fromHabeeb Okunade

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp