1. Overview

In this quick tutorial, we’ll learn how to inject a map from a YAML file in Spring Boot.

First, we’ll start with a little bit of insight on YAML files in Spring Framework. Then we’ll demonstrate how to bind YAML properties to a Map with a practical example.

2. YAML Files in Spring Framework

Using YAML files to store external configuration data is a common practice among Spring developers. Basically, Spring supports YAML documents as an alternative to properties, and uses SnakeYAML under the hood to parse them.

Without further ado, let’s see what a typical YAML file looks like:

server:
  port: 8090
  application:
    name: myapplication
    url: http://myapplication.com

As we can see, the YAML file is self-explanatory and more human-readable. As a matter of fact, YAML provides a fancy and concise way to store hierarchical configuration data.

By default, Spring Boot reads configuration properties from application.properties or application.yml at application startup. However, we can use @PropertySource to load a custom YAML file.

Now that we’re familiar with what a YAML file is, let’s see how to inject YAML properties as a Map in Spring Boot.

3. How to Inject a Map From a YAML File

Spring Boot has taken data externalization to the next level by providing a handy annotation called @ConfigurationProperties. This annotation is introduced to easily inject external properties from configuration files directly into Java objects.

In this section, we’ll focus on how to bind YAML properties into a bean class using the @ConfigurationProperties annotation*.*

First, we’ll define some key-value properties in application.yml:

server:
  application:
    name: InjectMapFromYAML
    url: http://injectmapfromyaml.dev
    description: How To Inject a map from a YAML File in Spring Boot
  config:
    ips:
      - 10.10.10.10
      - 10.10.10.11
      - 10.10.10.12
      - 10.10.10.13
    filesystem:
      - /dev/root
      - /dev/md2
      - /dev/md4
  users: 
    root:
      username: root
      password: rootpass
    guest:
      username: guest
      password: guestpass

In this example, we’ll try to map application into a simple Map<String, String>. Similarly, we’ll inject config details as a Map<String, List>, users as a Map with String keys, and objects belonging to a user-defined class (Credential) as values*.*

Then we’ll create a bean class, ServerProperties, to encapsulate the logic of binding our configuration properties to Maps:

@Component
@ConfigurationProperties(prefix = "server")
public class ServerProperties {

    private Map<String, String> application;
    private Map<String, List<String>> config;
    private Map<String, Credential> users;

    // getters and setters

    public static class Credential {
        
        private String username;
        private String password;
        
        // getters and setters
        
    }
}

As we can see, we decorated the ServerProperties class with @ConfigurationProperties. That way, we tell Spring to map all the properties with the specified prefix to an object of ServerProperties.

Recall that our app needs to be enabled for configuration properties as well, though this is done automatically in most Spring Boot applications.

Finally, we’ll test if our YAML properties are properly injected as Maps:

@RunWith(SpringRunner.class)
@SpringBootTest
class MapFromYamlIntegrationTest {

    @Autowired
    private ServerProperties serverProperties;

    @Test
    public void whenYamlFileProvidedThenInjectSimpleMap() {
        assertThat(serverProperties.getApplication())
          .containsOnlyKeys("name", "url", "description");

        assertThat(serverProperties.getApplication()
          .get("name")).isEqualTo("InjectMapFromYAML");
    }

    @Test
    public void whenYamlFileProvidedThenInjectComplexMap() {
        assertThat(serverProperties.getConfig()).hasSize(2);

        assertThat(serverProperties.getConfig()
          .get("ips")
          .get(0)).isEqualTo("10.10.10.10");

        assertThat(serverProperties.getUsers()
          .get("root")
          .getUsername()).isEqualTo("root");
    }

}

4. @ConfigurationProperties vs @Value

Now let’s do a quick comparison of @ConfigurationProperties and @Value.

Despite the fact that both annotations can be used to inject properties from configuration files*,* they are quite different. The major difference between these two annotations is that each one serves a different purpose.

In short, @V**alue allows us to directly inject a particular property value by its key. However, @ConfigurationProperties annotation binds multiple properties to a particular object, and provides access to the properties through the mapped object.

In general, Spring recommends using @ConfigurationProperties over @Value when it comes to injecting configuration data*. @ConfigurationProperties* offers a great way to centralize and group configuration properties in a structured object that we can inject later into other beans.

5. Conclusion

In this brief article, we discussed how to inject a Map from a YAML file in Spring Boot. Then we highlighted the difference between @ConfigurationProperties and @Value.

As usual, the complete source code for this article is available over on GitHub.