Spring Boot 加载自定义 YML 配置文件:从原理到实战
Spring Boot 默认支持 application.yml 配置文件的加载,但在需要拆分配置(如将业务配置与系统配置分离)时,我们常需加载自定义 YML 文件(如 custom.yml)。与 properties 文件不同,自定义 YML 文件无法直接通过 @PropertySource 加载(默认不支持 YML 解析),需通过自定义 PropertySourceFactory 实现。从 “原理分析→实现步骤→实战示例→注意事项” 四个维度,详细讲解自定义 YML 配置的加载方法。
核心问题:为何自定义 YML 无法直接加载?
Spring Boot 的 @PropertySource 注解默认仅支持 properties 格式 的配置文件(通过 DefaultPropertySourceFactory 解析),而 YML 文件采用缩进式语法,需要专用的解析器(如 YamlPropertiesFactoryBean)。
- properties 文件:键值对格式(
key=value),可直接被 @PropertySource 解析;
- YML 文件:层级结构(
key: value),需先转换为键值对格式(如 parent.child=value)才能被 Spring 识别。
因此,加载自定义 YML 文件的核心是:实现一个能将 YML 转换为键值对的 PropertySourceFactory。
解决方案:自定义 YML 解析工厂(PropertySourceFactory)
通过自定义 PropertySourceFactory,利用 Spring 内置的 YamlPropertiesFactoryBean 解析 YML 文件,将其转换为 Properties 对象(键值对格式),供 @PropertySource 使用。
1. 实现 YmlPropertySourceFactory
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.springframework.lang.Nullable;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.util.StringUtils;
import java.io.IOException;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class YmlPropertySourceFactory implements PropertySourceFactory {
private static final Logger log = LoggerFactory.getLogger(YmlPropertySourceFactory.class);
@Override
public PropertySource<?> createPropertySource(
@Nullable String name,
EncodedResource resource
) throws IOException {
Properties propertiesFromYaml = loadYamlIntoProperties(resource);
if (propertiesFromYaml == null) {
return (name != null)
? new PropertiesPropertySource(name, resource.getResource())
: new PropertiesPropertySource(resource.getResource().getFilename(), resource.getResource());
}
String sourceName = name;
if (!StringUtils.hasText(sourceName)) {
sourceName = resource.getResource().getFilename();
}
if (sourceName == null) {
log.error("无法获取配置文件名称: {}", resource);
throw new RuntimeException("加载 YML 配置文件失败:" + resource);
}
return new PropertiesPropertySource(sourceName, propertiesFromYaml);
}
private Properties loadYamlIntoProperties(EncodedResource resource) {
try {
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
factory.setResources(resource.getResource());
factory.afterPropertiesSet();
return factory.getObject();
} catch (IllegalStateException e) {
log.error("解析 YML 文件失败: {}", resource.getResource().getFilename(), e);
return null;
}
}
}