In modern applications, configuration plays a crucial role in maintaining flexibility and modularity. Often, we need to load properties from multiple YAML files, organized across nested directories, to ensure scalability and modular configuration. In this blog, we’ll explore how to dynamically load all YAML files from a specific directory structure and merge their properties into a single configuration object.
Use Case
Imagine we have a configuration directory like this:
src/main/resources
├── application.yml
├── configs
│ ├── environment
│ │ ├── dev.yml
│ │ ├── qa.yml
│ ├── features
│ │ ├── feature1.yml
│ │ ├── feature2.yml
We want to load all YAML files under the configs
directory (including subdirectories), merge them, and make the properties available for use in our Spring Boot application.
Implementation
1. Dynamic YAML Loader
We will create a utility class to dynamically load and merge all YAML files into a single Properties
object.
package com.example.config;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import java.io.IOException;
import java.util.Properties;
public class DynamicYamlLoader {
public static Properties loadYamlFiles(String basePath) throws IOException {
Properties combinedProperties = new Properties();
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
// Use '**' to include all nested directories and `.yml` files
Resource[] resources = resolver.getResources(basePath + "/**/*.yml");
for (Resource resource : resources) {
if (resource.exists() && resource.isReadable()) {
YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean();
yamlFactory.setResources(resource);
Properties yamlProperties = yamlFactory.getObject();
if (yamlProperties != null) {
combinedProperties.putAll(yamlProperties);
}
}
}
return combinedProperties;
}
}
2. Spring Configuration Loader
To integrate this loader into Spring, we’ll create a configuration class that loads the properties and makes them available in the application context.
package com.example.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;
import java.util.Properties;
@Configuration
public class AppConfig {
@Bean
public Properties dynamicProperties() throws IOException {
// Specify the base directory
String basePath = "classpath:/configs";
return DynamicYamlLoader.loadYamlFiles(basePath);
}
}
3. Example YAML Files
Here are some sample YAML files to simulate a realistic use case:
configs/environment/dev.yml
:
app:
name: DemoApp
environment: development
logging:
level: DEBUG
configs/features/feature1.yml
:
feature1:
enabled: true
maxRetries: 3
configs/features/feature2.yml
:
feature2:
enabled: false
timeout: 5000
4. Accessing Properties
You can now access the combined properties in your application. For example:
package com.example.service;
import org.springframework.stereotype.Service;
import java.util.Properties;
@Service
public class PropertyService {
private final Properties properties;
public PropertyService(Properties properties) {
this.properties = properties;
}
public void printProperties() {
System.out.println("App Name: " + properties.getProperty("app.name"));
System.out.println("Feature1 Enabled: " + properties.getProperty("feature1.enabled"));
System.out.println("Feature2 Timeout: " + properties.getProperty("feature2.timeout"));
}
}
When running the application, the output will be:
App Name: DemoApp
Feature1 Enabled: true
Feature2 Timeout: 5000
Explanation
Recursive File Loading:
- The
PathMatchingResourcePatternResolver
ensures all YAML files under the specified path (including subdirectories) are discovered.
- The
Merging Properties:
- Using
Properties.putAll()
allows us to combine properties from all YAML files. If duplicate keys exist, the last-loaded file overwrites the earlier values.
- Using
Spring Integration:
- By declaring a
@Bean
, we make the combined properties available for dependency injection.
- By declaring a
Comments
Post a Comment