Loading Multiple YAML Configuration Files in Spring Boot

In a Spring Boot application, configuration management plays a crucial role in adapting the application to different environments, such as development, testing, and production. YAML (YAML Ain't Markup Language) is a human - readable data serialization format commonly used in Spring Boot for configuration files. While Spring Boot automatically loads application.yml and environment - specific files like application - dev.yml, there are scenarios where you may need to load multiple custom YAML configuration files. This blog will provide a detailed guide on how to load multiple YAML configuration files in a Spring Boot application.

Table of Contents#

  1. Why Load Multiple YAML Configuration Files?
  2. Prerequisites
  3. Basic Setup of a Spring Boot Project
  4. Loading Multiple YAML Files Using @PropertySource
  5. Loading Multiple YAML Files Using YamlPropertySourceLoader
  6. Common Practices and Best Practices
  7. Example Usage
  8. Conclusion
  9. References

1. Why Load Multiple YAML Configuration Files?#

  • Separation of Concerns: You can separate different types of configurations into different files. For example, you can have one file for database configurations and another for security - related settings.
  • Environment - Specific Configurations: In addition to the standard application - {env}.yml files, you may have custom configurations that vary based on different environments.
  • Modularity: It allows for better modularity in your application. If you are working on a large - scale project, different modules can have their own configuration files.

2. Prerequisites#

  • Java Development Kit (JDK) 8 or higher
  • Apache Maven or Gradle for dependency management
  • Spring Boot Starter Parent in your project

3. Basic Setup of a Spring Boot Project#

You can create a Spring Boot project using Spring Initializr (https://start.spring.io/). Select the necessary dependencies such as Spring Web if you are building a web application.

Maven Example#

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5.4</version>
</parent>
 
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

Gradle Example#

plugins {
    id 'org.springframework.boot' version '2.5.4'
    id 'io.spring.dependency - management' version '1.0.11.RELEASE'
}
 
dependencies {
    implementation 'org.springframework.boot:spring - boot - starter - web'
}

4. Loading Multiple YAML Files Using @PropertySource#

The @PropertySource annotation is used to load properties from external files. By default, it does not support YAML files, but we can create a custom property source factory to make it work.

Custom Property Source Factory#

import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertySourceFactory;
import org.yaml.snakeyaml.Yaml;
 
import java.io.IOException;
import java.util.Map;
import java.util.Properties;
 
public class YamlPropertySourceFactory implements PropertySourceFactory {
    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
        Properties propertiesFromYaml = loadYamlIntoProperties(resource);
        String sourceName = name != null ? name : resource.getResource().getFilename();
        return new PropertiesPropertySource(sourceName, propertiesFromYaml);
    }
 
    private Properties loadYamlIntoProperties(EncodedResource resource) throws IOException {
        Yaml yaml = new Yaml();
        Map<String, Object> yamlMap = yaml.load(resource.getInputStream());
        Properties properties = new Properties();
        if (yamlMap != null) {
            flattenMap(yamlMap, properties, "");
        }
        return properties;
    }
 
    private void flattenMap(Map<String, Object> map, Properties properties, String parentKey) {
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            String key = parentKey.isEmpty() ? entry.getKey() : parentKey + "." + entry.getKey();
            if (entry.getValue() instanceof Map) {
                flattenMap((Map<String, Object>) entry.getValue(), properties, key);
            } else {
                properties.put(key, entry.getValue());
            }
        }
    }
}

Loading YAML Files#

import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.Configuration;
 
@Configuration
@PropertySource(value = {"classpath:custom - config.yml", "classpath:another - config.yml"}, factory = YamlPropertySourceFactory.class)
public class AppConfig {
    // Configuration class
}

5. Loading Multiple YAML Files Using YamlPropertySourceLoader#

The YamlPropertySourceLoader can also be used to load YAML files.

import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.DefaultPropertySourceFactory;
import org.springframework.core.io.support.EncodedResource;
 
import java.io.IOException;
 
public class CustomYamlPropertySourceFactory extends DefaultPropertySourceFactory {
    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
        if (resource == null) {
            return super.createPropertySource(name, resource);
        }
        YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
        return loader.load(name, resource.getResource()).get(0);
    }
}

And then use it in the configuration class:

import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.Configuration;
 
@Configuration
@PropertySource(value = {"classpath:custom - config.yml", "classpath:another - config.yml"}, factory = CustomYamlPropertySourceFactory.class)
public class CustomAppConfig {
    // Configuration class
}

6. Common Practices and Best Practices#

  • Naming Convention: Use a clear naming convention for your YAML files, such as module - config.yml or {env}-{module}-config.yml.
  • Error Handling: When loading multiple YAML files, handle potential errors such as file not found or invalid YAML syntax gracefully.
  • Order of Loading: Be aware of the order in which you load the YAML files. Properties in later - loaded files may override properties in earlier - loaded files.

7. Example Usage#

Let's assume we have two YAML files:

custom - config.yml#

database:
    url: jdbc:mysql://localhost:3306/mydb
    username: root
    password: password

another - config.yml#

api:
    base - url: https://example.com/api
    timeout: 5000

We can inject these properties into a service class:

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
 
@Service
public class MyService {
    @Value("${database.url}")
    private String databaseUrl;
 
    @Value("${api.base - url}")
    private String apiBaseUrl;
 
    public String getDatabaseUrl() {
        return databaseUrl;
    }
 
    public String getApiBaseUrl() {
        return apiBaseUrl;
    }
}

8. Conclusion#

Loading multiple YAML configuration files in a Spring Boot application provides more flexibility and better organization of your application's configurations. Whether you use @PropertySource with a custom property source factory or YamlPropertySourceLoader, you can easily manage different types of configurations in separate files.

9. References#