Spring Boot ConfigurationProperties and Profile Management Using YAML

From Properties to YAML

There are dozens of ways to handle externalized configuration in an application or service. Over the years Spring has provided quite a few, and recently the @Value and @Profile annotations have started to bring some sanity to the situation by attempting to minimize the developer’s interaction with the filesystem to read what should be readily available. With the advent of Spring Boot there are a couple new interesting twists – YAML files and @ConfigurationProperties.

Defining Configuration

First let’s look at YAML. The non-Java world has been using YAML format for quite some time, while the Java world appears to have been stuck on the .properties file and format. Properties files can now be a relic of the past if you so choose, as Spring Boot gives us the option to configure an application for all profiles within one file – application.yml. With the YAML file format you define sections that represent the different profiles. For example, consider this application.yml:

spring:
  profiles.active: default
---
spring:
  profiles: default
spring.datasource:
  driverClassName: com.mysql.jdbc.Driver
  url: jdbc:mysql://localhost:3306/myappdb?autoReconnect=true
  username: devuser
  password: devpassword
management:
  security:
    enabled: true
    role: ADMIN
doge:
  wow: 10
  such: so
  very: true
---
spring:
  profiles: qa
spring.datasource:
  url: jdbc:mysql://qa.myapp.com:3306/myappdb?autoReconnect=true
  username: qauser
  password: qapassword

Note a few things about this format – there’s a default active profile defined in the first section, followed by the default profile itself, followed by the qa profile (separated by “—“). There is no longer the namespacing format for each property, but an indentation-based markup to delineate hierarchy (yaml is space-sensitive). Also note that that in the qa profile we don’t re-define the management configuration, it is inherited, whereas we want to override the datasource url, password and user, but not the driver.

If we did nothing more than this the application configuration would default to the default profile, which would be fine in dev mode, but when deployed to the qa environment, we would pass the -Dspring.profiles.active=qa to the command line params for that profile to take effect. You can have multiple profiles alive at the same time too, otherwise it would have been called spring.profile.active 🙂 So who wins if there are multiple overrides? The lower you are in the yml file, the more override power you have, so that’s one way to think about how you organize your configuration.

This is all well and good, however what happens when the default profile is NOT a great profile for some of the development or QA team, and they want their own overrides? This is where you do need to resurrect the properties file, but this time for great justice. Your application.yml should be checked in, however you don’t want each and every team member checking in their own little overrides, or the file will get unwieldy and nobody will be happy. The trick is to create an application-<developername>.properties locally and exclude it (or all properties files) from your source control (.svnignore, .gitignore). With this file in the classpath you can reference the profile in your startup just as we did for qa, EG: -Dspring.profiles.active=default,dilbert.

But this trick is not just for developers. It’s never a good idea to check in production keys/tokens/secrets to your source control for all to see (unless you have absolutely no controls in place or don’t care) so this is a great mechanism for operations and/or SCM staff to have their own properties override file that contains all the sensitive content and let’s them control who has access to it.

Consuming Configuration

So how do we get access to all this fancy stuff from code? Sticking to the principals of keeping developers out of the business of managing what files to load and when, Spring Boot comes with even simpler support for references to the configuration file values, where you can create strongly typed beans to represent sections in the configuration. Let’s look at an example and then examine it.

@Configuration
@EnableConfigurationProperties
@ConfigurationProperties(prefix="doge")
  public class DogeSettings {
    private int wow;
    private String such;
    private boolean very;
 …getters and setters
 }

@Configuration tells Spring to treat this as a configuration class and register it as a Bean
@EnableConfigurationProperties tells Spring to treat this class as a consumer of application.yml/properties values
@ConfigurationProperties tells Spring what section this class represents.

Note that there is implicit type conversion for primitives and that this class is a Bean. That means you can @Autowire it into other Beans that require it’s values. EG:


@Autowired
private DogeSettings dogeSettings;
public boolean requiresDogeness() {
  if (dogeSettings.getWow() > 5 && dogeSettings.isVery == true) {
    return true;
  }
  return false;
}

Pretty easy and straightforward. It would be even more straightforward if @ConfigurationProperties was all you had to annotate, which the other two being implicit, but I leave that to the Spring team.

Testing

Using this scheme in a unit test environment is quite easy as well. You can of course define a unit-test profile with any overrides from the defaults you would use in such scenarios, and your @ConfigurationProperties classes will respect those, as long as you have the profile set. One easy way to do that is to create a TestConfig class and use it to set up all the test configuration overrides (such as beans, mocks, etc).


@Profile("unit-test")
@Configuration
@EnableJpaRepositories("com.myapp.repository")
@ComponentScan({"com.myapp"})
@EnableAutoConfiguration
public class TestConfig {
  @Bean
  public DataSource dataSource() {
    return new EmbeddedDatabaseBuilder().setType(H2).build();
  }
}

Note the @Profile annotation to set the context for this configuration. Without it we could accidentally have this configuration in our production configuration… The rest is just boilerplate setup that is optional depending on what you want to test. From the test itself you can then leverage this:


@ActiveProfiles("unit-test")
@SpringApplicationConfiguration(classes = {TestConfig.class})
public class DogenessTest extends AbstractTestNGSpringContextTests {

  @Autowired
  private DogeSettings dogeSettings;

  @Test
  public void testRequiresDogeness() {
    ...
  }
}

With @ActiveProfiles we can now isolate the configuration for both the application properties as well as any bean config. The @SpringApplicationConfiguration gives us an annotation-based spring context to work with for the execution of the tests.

Conclusion

If you have the luxury of starting a project from scratch, it’s worth giving this approach a try.   There’s very little code required, no static access to config utilitites, and you get strongly typed configuration objects that you can inject anywhere you need them.  To see some of this code in action, check out my project in GitHub.

References

Advertisements
Spring Boot ConfigurationProperties and Profile Management Using YAML

7 thoughts on “Spring Boot ConfigurationProperties and Profile Management Using YAML

  1. stephanenicoll says:

    Quick question: why do you add `@Configuration` and `@EnableConfigurationProperties` to your `DogeSettings`?

    Configuration isn’t necessary (and a bit confusing if you ask me) and you would put `@EnableConfigurationProperties` on your main configuration class for doge (typically `DogeConfig`. It’s a bit backward to ask to enable X on X itself.

    Does that make sense? Thanks!

    1. Tim says:

      Hi Stéphane, I believe that is simply legacy, early days in Boot history led me to examples that used this pattern, perhaps it was simplified since. I”ll clean that up and thanks for the feedback!

  2. Hi Tim,

    Thanks for this article – very informative.

    Question: why do you recommend using application-.properties rather than application-.yml to incorporate developer-specific configuration data (and sensitive/secret configuration data). My understanding (from the Spring Boot documentation) is that yml files could be used in this way as well.

    1. Tim says:

      Hi Andrew, it’s mostly to avoid individual developer settings going into source control (same goes for production settings). Those really need to be separate and overridden.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s