0%

加载自定义yml配置文件

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;

/**
* 自定义 YML 配置文件解析工厂,支持 @PropertySource 加载 YML 文件
*/
public class YmlPropertySourceFactory implements PropertySourceFactory {

private static final Logger log = LoggerFactory.getLogger(YmlPropertySourceFactory.class);

@Override
public PropertySource<?> createPropertySource(
@Nullable String name, // 配置源名称(可为 null,自动生成)
EncodedResource resource // 资源对象(包含 YML 文件信息)
) throws IOException {
// 1. 将 YML 文件解析为 Properties 对象(键值对格式)
Properties propertiesFromYaml = loadYamlIntoProperties(resource);

// 2. 处理解析结果(若解析失败,使用默认资源加载方式)
if (propertiesFromYaml == null) {
return (name != null)
? new PropertiesPropertySource(name, resource.getResource())
: new PropertiesPropertySource(resource.getResource().getFilename(), resource.getResource());
}

// 3. 生成配置源名称(优先使用传入的 name,否则使用文件名)
String sourceName = name;
if (!StringUtils.hasText(sourceName)) {
sourceName = resource.getResource().getFilename();
}
if (sourceName == null) {
log.error("无法获取配置文件名称: {}", resource);
throw new RuntimeException("加载 YML 配置文件失败:" + resource);
}

// 4. 返回 Properties 格式的配置源
return new PropertiesPropertySource(sourceName, propertiesFromYaml);
}

/**
* 将 YML 资源解析为 Properties 对象
*/
private Properties loadYamlIntoProperties(EncodedResource resource) {
try {
// 使用 Spring 内置的 YamlPropertiesFactoryBean 解析 YML
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
factory.setResources(resource.getResource()); // 设置 YML 资源
factory.afterPropertiesSet(); // 初始化解析器
return factory.getObject(); // 获取解析后的 Properties
} 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
# 自定义 YML 配置示例
custom:
name: "订单服务"
type-fields: # 映射为 Map<String, String>
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;

/**
* 自定义 YML 配置绑定类
*/
@Data // Lombok 注解,自动生成 getter/setter
@Component // 注册为 Spring Bean
@PropertySource(
value = "classpath:custom.yml", // 自定义 YML 文件路径
factory = YmlPropertySourceFactory.class // 指定自定义解析工厂
)
@ConfigurationProperties(prefix = "custom") // 绑定前缀为 "custom" 的配置
public class CustomProperties {
private String name; // 对应 custom.name
private Map<String, String> typeFields; // 对应 custom.type-fields(YML 中横杠自动映射驼峰)
private DbConfig db; // 对应 custom.db(嵌套结构)

// 嵌套配置类(对应 custom.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;
// 输出结果:
// {
// "name": "订单服务",
// "typeFields": {"key1":"value1", "key2":"value2"},
// "db": {"url":"jdbc:mysql://localhost:3306/custom_db", "username":"root"}
// }
}
}

进阶场景:多环境与多文件加载

1. 多环境自定义配置(如 custom-dev.ymlcustom-prod.yml

(1)创建多环境 YML 文件
1
2
3
4
src/main/resources/
├── custom-dev.yml # 开发环境配置
├── custom-prod.yml # 生产环境配置
└── custom.yml # 通用配置(多环境共享)
(2)根据激活的环境加载对应文件

通过 @PropertySourcevalue 属性支持 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 文件

通过 @PropertySourcevalue 数组指定多个文件,优先级按顺序递增(后加载的覆盖先加载的):

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

问题原因:
  • @PropertySourcevalue 路径错误(如拼写错误、文件不在 classpath 下);
  • 多模块项目中,配置文件放置位置错误(需放在主模块的 src/main/resources 下)。
解决方案:
  • 检查路径是否正确(如 classpath:custom.yml 表示文件在 src/main/resources 根目录);
  • 多模块项目中,确保配置文件位于 启动类所在模块resources 目录。

3. 配置覆盖顺序

Spring Boot 配置加载存在优先级,自定义 YML 可能被其他配置覆盖,优先级从高到低为:

  1. 命令行参数(如 java -jar app.jar --custom.name=test);
  2. 系统环境变量;
  3. application-{profile}.yml(环境特定配置);
  4. application.yml(全局配置);
  5. 自定义 YML 文件(如 custom.yml);
  6. 类路径下的 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 为键值对格式,关键步骤可概括为:

  1. 实现解析工厂:自定义 YmlPropertySourceFactory,利用 YamlPropertiesFactoryBean 解析 YML;
  2. 创建配置文件:在 resources 目录下创建自定义 YML(如 custom.yml);
  3. 绑定配置类:通过 @PropertySource(factory=...)@ConfigurationProperties 绑定配置;
  4. 注入使用:将配置类注入到业务组件,直接使用绑定的字段

欢迎关注我的其它发布渠道

表情 | 预览
快来做第一个评论的人吧~
Powered By Valine
v1.3.10