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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| 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; } } }
|
核心逻辑:
- 利用
YamlPropertiesFactoryBean 将 YML 层级结构转换为 Properties 键值对(如 custom.name=张三);
- 兼容解析失败的场景,降级为默认资源加载方式;
- 自动生成配置源名称,确保 Spring 能正确识别配置来源。
2. 使用自定义 YML 配置
(1)创建自定义 YML 文件(src/main/resources/custom.yml)
1 2 3 4 5 6 7 8 9
| custom: name: "订单服务" type-fields: key1: "value1" key2: "value2" db: url: "jdbc:mysql://localhost:3306/custom_db" username: "root"
|
(2)定义配置类,绑定 YML 配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.PropertySource; import org.springframework.stereotype.Component; import lombok.Data; import java.util.Map;
@Data @Component @PropertySource( value = "classpath:custom.yml", // 自定义 YML 文件路径 factory = YmlPropertySourceFactory.class // 指定自定义解析工厂 ) @ConfigurationProperties(prefix = "custom") public class CustomProperties { private String name; private Map<String, String> typeFields; private DbConfig db;
@Data public static class DbConfig { private String url; private String username; } }
|
关键注解说明:
@PropertySource:指定自定义 YML 文件路径,并通过 factory 指定解析工厂;
@ConfigurationProperties(prefix = "custom"):将前缀为 custom 的配置绑定到当前类字段;
- 字段名映射规则:YML 中的
type-fields 自动映射到驼峰命名的 typeFields。
(3)使用配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController;
@RestController public class CustomConfigController {
@Autowired private CustomProperties customProperties;
@GetMapping("/custom-config") public CustomProperties getCustomConfig() { return customProperties; } }
|
进阶场景:多环境与多文件加载
1. 多环境自定义配置(如 custom-dev.yml、custom-prod.yml)
(1)创建多环境 YML 文件
1 2 3 4
| src/main/resources/ ├── custom-dev.yml # 开发环境配置 ├── custom-prod.yml # 生产环境配置 └── custom.yml # 通用配置(多环境共享)
|
(2)根据激活的环境加载对应文件
通过 @PropertySource 的 value 属性支持 SpEL 表达式,动态加载环境相关的配置:
1 2 3 4 5 6 7 8 9
| @PropertySource( value = "classpath:custom-${spring.profiles.active:default}.yml", // 动态路径 factory = YmlPropertySourceFactory.class ) @ConfigurationProperties(prefix = "custom") @Component public class CustomProperties { }
|
- 若激活
dev 环境(spring.profiles.active=dev),则加载 custom-dev.yml;
- 若未指定环境,默认加载
custom-default.yml(需提前创建)。
2. 加载多个自定义 YML 文件
通过 @PropertySource 的 value 数组指定多个文件,优先级按顺序递增(后加载的覆盖先加载的):
1 2 3 4 5 6 7 8 9 10 11 12
| @PropertySource( value = { "classpath:custom-base.yml", // 基础配置(低优先级) "classpath:custom-business.yml" // 业务配置(高优先级,覆盖基础配置) }, factory = YmlPropertySourceFactory.class ) @ConfigurationProperties(prefix = "custom") @Component public class CustomProperties { }
|
注意事项与常见问题
1. 字段映射失败(如 YML 有值但配置类字段为 null)
问题原因:
- 字段名与 YML 配置不匹配(未遵循驼峰映射规则,如 YML 是
type-fields,类字段应为 typeFields);
- 未添加
@Component 或 @Configuration 注解,配置类未被 Spring 扫描;
- 未生成 getter/setter 方法(Lombok 的
@Data 注解可自动生成,若手动编写需确保方法存在)。
解决方案:
- 严格遵循 “YML 横杠命名→类驼峰命名” 的映射规则;
- 确保配置类被 Spring 扫描(添加
@Component 或在配置类上标注 @Configuration);
- 检查 getter/setter 是否存在(可通过 IDE 生成或使用 Lombok 注解)。
2. 配置文件路径错误(FileNotFoundException)
问题原因:
@PropertySource 的 value 路径错误(如拼写错误、文件不在 classpath 下);
- 多模块项目中,配置文件放置位置错误(需放在主模块的
src/main/resources 下)。
解决方案:
- 检查路径是否正确(如
classpath:custom.yml 表示文件在 src/main/resources 根目录);
- 多模块项目中,确保配置文件位于 启动类所在模块 的
resources 目录。
3. 配置覆盖顺序
Spring Boot 配置加载存在优先级,自定义 YML 可能被其他配置覆盖,优先级从高到低为:
- 命令行参数(如
java -jar app.jar --custom.name=test);
- 系统环境变量;
application-{profile}.yml(环境特定配置);
application.yml(全局配置);
- 自定义 YML 文件(如
custom.yml);
- 类路径下的
default.properties。
注意:若需自定义 YML 优先级高于 application.yml,可通过 @Order 注解调整,但不推荐(易导致配置混乱)。
4. 支持 YAML 列表(List)配置
YML 支持列表结构,配置类中可用 List 接收:
(1)YML 配置:
1 2 3 4 5
| custom: servers: - "192.168.1.100" - "192.168.1.101" - "192.168.1.102"
|
(2)配置类字段:
1 2 3 4 5 6 7
| @Data @Component @PropertySource(value = "classpath:custom.yml", factory = YmlPropertySourceFactory.class) @ConfigurationProperties(prefix = "custom") public class CustomProperties { private List<String> servers; }
|
总结
加载自定义 YML 配置文件的核心是通过 YmlPropertySourceFactory 解析 YML 为键值对格式,关键步骤可概括为:
- 实现解析工厂:自定义
YmlPropertySourceFactory,利用 YamlPropertiesFactoryBean 解析 YML;
- 创建配置文件:在
resources 目录下创建自定义 YML(如 custom.yml);
- 绑定配置类:通过
@PropertySource(factory=...) 和 @ConfigurationProperties 绑定配置;
- 注入使用:将配置类注入到业务组件,直接使用绑定的字段
v1.3.10