Spring Boot @ConfigurationProperties example

spring-boot-configurationproperties

Spring Boot @ConfigurationProperties is letting developer maps the entire file into an object easily.

1. Simple Properties file

Normally, you use the @Value annotation to inject the .properties value one by one, this is good for small and simple structure .properties files.

global.properties

email=test@mkyong.com
thread-pool=12

1.1 @Value example.

GlobalProperties.java

@Component
@PropertySource("classpath:global.properties")
public class GlobalProperties {

    @Value("${thread-pool}")
    private int threadPool;

    @Value("${email}")
    private String email;
    
    //getters and setters

}

1.2 @ConfigurationProperties example.

GlobalProperties.java

import org.springframework.boot.context.properties.ConfigurationProperties;

@Component
@PropertySource("classpath:global.properties")
@ConfigurationProperties
public class GlobalProperties {

    private int threadPool;
    private String email;

    //getters and setters

}

2. Complex Properties file

2.1 Review a complex structure .properties file below, how you map the values via @Value annotation?

application.properties

#Logging
logging.level.org.springframework.web=ERROR
logging.level.com.mkyong=DEBUG

#Global
email=test@mkyong.com
thread-pool=10

#App
app.menus[0].title=Home
app.menus[0].name=Home
app.menus[0].path=/
app.menus[1].title=Login
app.menus[1].name=Login
app.menus[1].path=/login

app.compiler.timeout=5
app.compiler.output-folder=/temp/

app.error=/error/

or the equilvent in YAML.

application.yml

logging:
  level:
    org.springframework.web: ERROR
    com.mkyong: DEBUG
email: test@mkyong.com
thread-pool: 10
app:
  menus:
    - title: Home
      name: Home
      path: /
    - title: Login
      name: Login
      path: /login
  compiler:
    timeout: 5
    output-folder: /temp/
  error: /error/
Note
@ConfigurationProperties supports both .properties and .yml file.

2.2 @ConfigurationProperties come to rescue, create a @ConfigurationProperties bean like this :

AppProperties.java

package com.mkyong;

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

import java.util.ArrayList;
import java.util.List;

@Component
@ConfigurationProperties("app") // prefix app, find app.* values
public class AppProperties {

    private String error;
    private List<Menu> menus = new ArrayList<>();
    private Compiler compiler = new Compiler();

    public static class Menu {
        private String name;
        private String path;
        private String title;

        //getters and setters

        @Override
        public String toString() {
            return "Menu{" +
                    "name='" + name + '\'' +
                    ", path='" + path + '\'' +
                    ", title='" + title + '\'' +
                    '}';
        }
    }

    public static class Compiler {
        private String timeout;
        private String outputFolder;

        //getters and setters

        @Override
        public String toString() {
            return "Compiler{" +
                    "timeout='" + timeout + '\'' +
                    ", outputFolder='" + outputFolder + '\'' +
                    '}';
        }

    }

    //getters and setters
}
GlobalProperties.java

package com.mkyong;

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

@Component
@ConfigurationProperties // no prefix, find root level values.
public class GlobalProperties {

    private int threadPool;
    private String email;

	//getters and setters
}

3. Demo

3.1 Tests to make sure the .properties values are mapped to the object correctly.

WelcomeController.java

package com.mkyong;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.Map;

@Controller
public class WelcomeController {

    private static final Logger logger = LoggerFactory.getLogger(WelcomeController.class);

    private AppProperties app;
    private GlobalProperties global;

    @Autowired
    public void setApp(AppProperties app) {
        this.app = app;
    }

    @Autowired
    public void setGlobal(GlobalProperties global) {
        this.global = global;
    }

    @RequestMapping("/")
    public String welcome(Map<String, Object> model) {

        String appProperties = app.toString();
        String globalProperties = global.toString();

        logger.debug("Welcome {}, {}", app, global);

        model.put("message", appProperties + globalProperties);
        return "welcome";
    }

}
SpringBootWebApplication.java

package com.mkyong;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootWebApplication {

    public static void main(String[] args) throws Exception {
        SpringApplication.run(SpringBootWebApplication.class, args);
    }

}

3.1 Start Spring Boot with mvn spring-boot:run and access the default / controller, review the console :

Console

Welcome 
	AppProperties{error='/error/', 
		menus=[
		    Menu{name='Home', path='/', title='Home'}, 
		    Menu{name='Login', path='/login', title='Login'}
		], 
		compiler=Compiler{timeout='5', outputFolder='/temp/'}}, 
		
	GlobalProperties{threadPool=10, email='test@mkyong.com'}

4. @ConfigurationProperties Validation

4.1 This @ConfigurationProperties support JSR-303 bean validation – javax.validation

GlobalProperties.java

package com.mkyong;

import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import javax.validation.constraints.Max;
import javax.validation.constraints.Min;

@Component
@ConfigurationProperties
public class GlobalProperties {

    @Max(5)
    @Min(0)
    private int threadPool;

    @NotEmpty
    private String email;

    //getters and setters
}

4.2 Try start Spring Boot again, and review the console :

Console

***************************
APPLICATION FAILED TO START
***************************

Description:

Binding to target GlobalProperties{threadPool=10, email='test@mkyong.com'} failed:

    Property: target.threadPool
    Value: 10
    Reason: must be less than or equal to 5


Action:

Update your application's configuration

//...
Note
For more detail, please refer to this official Spring Boot Externalized Configuration

Download Source Code

Download it – spring-boot-properties.zip (15 KB)

References

  1. Spring Boot – Externalized Configuration
  2. Spring @PropertySource example
  3. JSR 303: Bean Validation

About the Author

author image
mkyong
Founder of Mkyong.com, love Java and open source stuff. Follow him on Twitter, or befriend him on Facebook or Google Plus. If you like my tutorials, consider make a donation to these charities.

Comments

Leave a Reply

avatar
newest oldest most voted
Jeff
Guest
Jeff

Nice article! Just a simple question, when you are working with @ConfigurationProperties, the variable names must match the properties keys, right? What about names like “thread-pool” that you have used threadPool, it identify it automatically? Thanks!

mkyong
Guest
mkyong

yes, In Spring, this called relaxed binding
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-relaxed-binding

Both ‘thread-pool’ and ‘thread_pool’ will map to threadPool property.

Jeff
Guest
Jeff

Thank you! Really helps!

Carol
Guest
Carol

Thanks

Thien Nguyen
Guest
Thien Nguyen

How would you change this to use a config server instead of a properties file?

Shawn
Guest
Shawn

Hi Mkyong, when I use @ConfigurationProperties(prefix=”app”) with static @Bean getPropertyPlaceholderConfigurer(). it seems not working for mapping the keys in properties files, do you have any idea how to make this working? I am trying to load property files from a folder.

Pablo
Guest
Pablo

prefix not working. I have following in my java class:

@Component
@PropertySource(“classpath:custom-cas.properties”)
@ConfigurationProperties(prefix = “myprefix”)
public class CustomProperties {
//properties and getters/setters
}

and my custom-cas-properties file has:
myprefix.dummyValue=100
myprefix.isEnabled=True

but values are coming up null when I read them using
System.out.println(“Read dummyValue: “+ env.getProperty(“dummyValue”) );

If I remove the “myprefix” references i can read my properties.

What am I doing wrong?

Ansuman
Guest
Ansuman

@pablo If am not wrong, you can use either @PropertySource, where you have specify your file name or You can use @ConfigurationProperties(“custom”) this will search your file in your classpath .

peethanAbhilash
Guest
peethanAbhilash

Hi : Thanks, as usual your post are quite precise and informative.
One question – is there any way I can populate Spring-Config.xml parameter values from application properties ?

For eg : application.properties contains a value defined as “eas.ssl.ciphers=JKS”
and in config xml I need to apply as ${eas.ssl.ciphers}

Though in log I could see the application.properties are loaded as expected – it seems they are not applied within config.xml file as I get error no value found ?

Is this a valid case ?

Nitin
Guest
Nitin

Thanks for nice article Mkyong, but I have a question. I want to have some defaults in case properties are not mentioned in properties file. How can I get defaults along with @ConfigurationProperties. I was reading about @Value annotation which can provide defaults option, but these two are not mixing up together and I am getting error.

Igor
Guest
Igor
As of Spring Boot 1.5.4 (maybe even earlier) YML files are not processed correctly. They are parsed as regular properties files. In order to fix that: {code} public class YamlPropertySourceFactory implements PropertySourceFactory { @Override public PropertySource createPropertySource(String name, EncodedResource resource) throws IOException { return name != null ? new PropertySourcesLoader().load(resource.getResource(), name, null) : new PropertySourcesLoader().load( resource.getResource(), getNameForResource(resource.getResource()), null); } private static String getNameForResource(Resource resource) { String name = resource.getDescription(); if (!StringUtils.hasText(name)) { name = resource.getClass().getSimpleName() + “@” + System.identityHashCode(resource); } return name; } } {code}
Igor
Guest
Igor

Add this factory class to your property source declaration:

e.g. @PropertySource(value = “classpath:yamlfile.yml”, factory = YamlPropertySourceFactory.class)

Doug
Guest
Doug

Thanks for this, very helpful. What would be the best approach to test a service that is injecting the class with the @ConfigurationProperties as a dependency? How should we go about to mock the properties?

Chinnu Manju
Guest
Chinnu Manju

Thanks Mkyong for the nice article.
This really helped and made my work easier.
I also tried doing the same with Maps, map with String as Key and Menu as value. But that didnt work at all. I tried multiple things, but it didnt help. Could you please assist on how can I parse the yml values into a map with custom object?

Chinnu Manju
Guest
Chinnu Manju

@mkyong:disqus please help me with the above question. I am still stuck with this issue.

j3j3 Hype!!
Guest
j3j3 Hype!!

wahaha