Wednesday, September 17, 2014

Using @ConfigurationProperties in Spring Boot

In my latest blog post I described shortly how one can configure mail in Spring Boot application. To inject properties into the configuration I used Spring’s @Value annotation. But Spring Boot provides an alternative method of working with properties that allows strongly typed beans to govern and validate the configuration of your application. In this post I will demonstrate how to utilize @ConfigurationProperties while configuring the application.

Update 20/05/2016: The code was updated to reflect the newest Spring Boot.

So we want to use the mail configuration as an example. The configuration file is placed in a separate file, called mail.properties. The properties must be named using a proper convention, so they can be bind properly. Let’s see some examples:

  • protocol and PROTOCOL will be bind to protocol field of a bean
  • smtp-auth, smtp_auth, smtpAuth will be bind to smtpAuth field of a bean
  • smtp.auth will be bind to … hmm to smtp.auth field of a bean!

Spring Boot uses some relaxed rules for binding properties to @ConfigurationProperties beans and supports hierarchical structure.

So let’s create a @ConfigurationProperties bean:

Note: As of Spring Boot 1.2 (December 2014) there is org.springframework.boot.autoconfigure.mail.MailProperties class similar to the below.

@ConfigurationProperties(locations = "classpath:mail.properties", ignoreUnknownFields = false, prefix = "mail")
public class MailProperties {

    public static class Smtp {

        private boolean auth;
        private boolean starttlsEnable;

        // ... getters and setters
    }

    @NotBlank
    private String host;
    private int port;  
    private String from;
    private String username;
    private String password;
    @NotNull
    private Smtp smtp;

    // ... getters and setters

}

… that should be created from the following properties (mail.properties):

mail.host=localhost
mail.port=25
mail.smtp.auth=false
mail.smtp.starttls-enable=false
mail.from=me@localhost
mail.username=
mail.password=

In the above example, we annotated a bean with @ConfigurationPropertiesso that Spring Boot can bind properties to it. ignoreUnknownFields = false tells Spring Boot to throw an exception when there are properties that do not match a declared field in the bean. This is pretty handy during the development! prefix let you select the name prefix of the properties to bind.

Please note that setters and getters should be created in @ConfigurationProperties bean! And opposite to @Value annotation it may bring some extra noise to the code (especially in simple cases, in my opinion).

Ok, but we want to use the properties to configure our application. There are at least two ways of creating @ConfigurationProperties. We can either use it together with @Configuration that provides @Beans or we can use it separately and inject into @Configuration bean.

The first scenario:

@Configuration
@ConfigurationProperties(locations = "classpath:mail.properties", prefix = "mail")
public class MailConfiguration {

    public static class Smtp {

        private boolean auth;
        private boolean starttlsEnable;

        // ... getters and setters
    }

    @NotBlank
    private String host;
    private int port;  
    private String from;
    private String username;
    private String password;
    @NotNull
    private Smtp smtp;

    // ... getters and setters   

    @Bean
    public JavaMailSender javaMailSender() {
        // omitted for readability
    }
}

In the second scenario, we simply annotate the properties bean (as above) and use Spring’s@Autowireto inject it into mail configuration bean:

@Configuration
public class MailConfiguration {

    @Autowired
    private MailProperties mailProperties;

    @Bean
    public JavaMailSender javaMailSender() {
        // omitted for readability
    }
}

Note: The below is no more valid in Spring Boot. @ConfigurationProperties are enabled by default.

Please note @EnableConfigurationPropertiesannotation. This annotation tells Spring Boot to enable support for @ConfigurationProperties of a specified type. If not specified, you may otherwise see the org.springframework.beans.factory.NoSuchBeanDefinitionException exception.

To sum up, @ConfigurationProperties beans are pretty handy. Is it better than using @Value annotation? In certain scenarios probably yes, but this is just the choice you need to make.

Have a look at Spring Boot’s documentation to read more about typesafe configuration properties: http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-external-config-typesafe-configuration-properties

See also: ${… } placeholders support in @Value annotations in Spring

10 comments:

  1. > smtp.auth will be bind to … hmm to smtp.auth field of a bean!

    Maybe you meant "... to 'auth' field of bean named 'smtp'"

    ReplyDelete
  2. Bardzo przyjemny post, dziękuję Kolego. Useful and well done, thnx!

    ReplyDelete
  3. Is there a way to utilize @ConfigurationProperties during unit testing?

    ReplyDelete
  4. @Philipp, U need to annotate your test class like this for spring to load in junit @runwith(springjunit4classrunner.class)
    @ContextConfiguration(locations={"spring-context.xml"})

    ReplyDelete
  5. @Configuration will add the properties to spring context,and @EnableConfigurationProperties will add once again,so duplicate bean in context

    ReplyDelete
    Replies
    1. As far as I am concerned, it wasn't always like that. Fixing. Thanks for feedback.

      Delete