1. Overview
In this quick tutorial, we're going to take a close look at 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 showcase, through a practical example, how to bind YAML properties to a Map.
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 are 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're going to cover in-depth how to bind YAML properties into a bean class using the @ConfigurationProperties annotation.
First, let's 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<String>>, and users as a Map with String keys and objects belonging to a user-defined class – Credential – as values.
Second, let's 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 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, let's 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, @Value allows us to inject directly 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
To sum it up, we first explained 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 the article is available over on GitHub.