Yaml 语法

该部分基于菜鸟教程 https://www.runoob.com/w3cnote/yaml-intro.html

yaml 概述

YAML 是 “YAML Ain’t a Markup Language”(YAML 不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:“Yet Another Markup Language”(仍是一种标记语言)。

YAML 是一种人类可读的数据序列化标准,常用于配置文件、数据交换、API规范等场景。它的设计目标是既能被人类轻松阅读和编写,又能被机器有效解析。

YAML 的语法和其他高级语言类似,并且可以简单表达清单、散列表,标量等数据形态。它使用空白符号缩进和大量依赖外观的特色,特别适合用来表达或编辑数据结构、各种配置文件、倾印调试内容、文件大纲(例如:许多电子邮件标题格式和YAML非常接近)。

YAML 的配置文件后缀为 .yml,如:application.yml

YAML 的主要特点:

  • 人类可读性强,语法简洁直观
  • 使用缩进表示层级结构,避免了XML的标签冗余
  • 支持丰富的数据类型
  • 跨语言支持,几乎所有编程语言都有对应的解析库
  • 配置文件后缀为 .yml.yaml

基本语法

  • 大小写敏感 - Namename 是不同的键
  • 使用缩进表示层级关系 - 类似Python的语法风格
  • 缩进只允许使用空格,不允许使用Tab - 这是YAML的强制要求
  • 缩进的空格数量不限 - 但同一层级必须左对齐
  • 冒号后必须跟空格 - key: value,不能写成key:value
  • 井号表示注释 - # 这是注释

数据类型

YAML 支持以下几种数据类型:

  • 对象:键值对的集合,又称为映射(mapping)/ 哈希(hashes) / 字典(dictionary)

  • 数组:一组按次序排列的值,又称为序列(sequence) / 列表(list)

  • 纯量 (标量)(scalars):基本数据类型

YAML 对象

对象键值对使用冒号结构表示 key: value,冒号后面要加一个空格。

也可以使用 key:{key1: value1, key2: value2, …}

还可以使用缩进表示层级关系;

1
2
3
4
5
# 简单的键值对
user:
name: Alice
age: 30
email: alice@example.com
1
2
3
4
5
6
7
8
9
10
11
12
# 嵌套映射
company:
name: Tech Corp
address:
street: 123 Main St
city: San Francisco
country: USA
employees:
- name: Alice
position: Developer
- name: Bob
position: Designer

较为复杂的对象格式,可以使用问号加一个空格代表一个复杂的 key,配合一个冒号加一个空格代表一个 value:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 使用引号包围复杂键名
"user name": John
"api-key": abc123
"@context": "https://schema.org"

# 使用问号语法定义复杂键
? [key1, key2]
: value_for_complex_key

?
name: complex_object_key
type: identifier
:
result: success
data: processed

意思即对象的属性是一个数组 [complexkey1,complexkey2],对应的值也是一个数组 [complexvalue1,complexvalue2]

而且还支持一种行内映射的形式

1
2
3
# 行内格式(类似JSON)
user: {name: Alice, age: 30, city: "New York"}
coordinates: {x: 10, y: 20, z: 5}

YAML 数组

以 - 开头的行表示构成一个数组:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 使用短横线表示数组元素
languages:
- Python
- JavaScript
- Go
- Rust

# 嵌套序列
projects:
- name: Project A
languages:
- Python
- Django
status: active
- name: Project B
languages:
- JavaScript
- React
status: completed

YAML 支持多维数组,可以使用行内表示:

1
2
3
4
# 行内数组格式
numbers: [1, 2, 3, 4, 5]
colors: ["red", "green", "blue"]
mixed: [1, "hello", true, null]xxxxxxxxxx4 1# 行内数组格式2numbers: [1, 2, 3, 4, 5]3colors: ["red", "green", "blue"]4mixed: [1, "hello", true, null]key: [value1, value2, ...]yanl

多维序列

数据结构的子成员是一个数组,则可以在该项下面缩进一个空格。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 矩阵/二维数组
matrix:
- [1, 2, 3]
- [4, 5, 6]
- [7, 8, 9]

# 或者使用嵌套格式
matrix_nested:
-
- 1
- 2
- 3
-
- 4
- 5
- 6

一个相对复杂的例子:

1
2
3
4
5
6
7
8
9
companies:
-
id: 1
name: company1
price: 200W
-
id: 2
name: company2
price: 500W

意思是 companies 属性是一个数组,每一个数组元素又是由 id、name、price 三个属性构成。

数组也可以使用流式(flow)的方式表示:

1
companies: [{id: 1,name: company1,price: 200W},{id: 2,name: company2,price: 500W}]

复合结构

数组和对象可以构成复合结构,例:

1
2
3
4
5
6
7
8
9
languages:
- Ruby
- Perl
- Python
websites:
YAML: yaml.org
Ruby: ruby-lang.org
Python: python.org
Perl: use.perl.org

转换为 json 为:

1
2
3
4
5
6
7
8
9
{ 
languages: [ 'Ruby', 'Perl', 'Python'],
websites: {
YAML: 'yaml.org',
Ruby: 'ruby-lang.org',
Python: 'python.org',
Perl: 'use.perl.org'
}
}

纯量

纯量是最基本的,不可再分的值,包括:

  • 字符串
  • 布尔值
  • 整数
  • 浮点数
  • Null
  • 时间
  • 日期

使用一个例子来快速了解纯量的基本使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
boolean: 
- TRUE #true,True都可以
- FALSE #false,False都可以
float:
- 3.14
- 6.8523015e+5 #可以使用科学计数法
int:
- 123
- 0b1010_0111_0100_1010_1110 #二进制表示
null:
nodeName: 'node'
parent: ~ #使用~表示null
string:
- 哈哈
- 'Hello world' #可以使用双引号或者单引号包裹特殊字符
- newline
newline2 #字符串可以拆成多行,每一行会被转化成一个空格
date:
- 2018-02-17 #日期必须使用ISO 8601格式,即yyyy-MM-dd
datetime:
- 2018-02-17T15:02:31+08:00 #时间使用ISO 8601格式,时间和日期之间使用T连接,最后使用+代表时区

引用

锚点和引用是YAML中避免重复配置的强大功能。它类似于编程中的变量定义和使用。

  • 锚点(Anchor):使用 & 符号定义,相当于给一段配置起个名字
  • 引用(Reference):使用 * 符号引用,相当于使用之前定义的配置
  • 合并(Merge):使用 << 符号,将引用的内容合并到当前位置

简单值的引用

1
2
3
4
5
6
7
8
9
10
11
12
13
# 定义锚点 - 给值起个名字
database_host: &db_host "localhost"
cache_host: &cache_host "redis.example.com"

# 使用引用 - 复用之前定义的值
services:
api:
database_url: *db_host # 相当于 "localhost"
cache_url: *cache_host # 相当于 "redis.example.com"

worker:
database_url: *db_host # 同样是 "localhost"
cache_url: *cache_host # 同样是 "redis.example.com"

相当于

1
2
3
4
5
6
7
8
9
10
11
12
# 等价于以下配置
database_host: "localhost"
cache_host: "redis.example.com"

services:
api:
database_url: "localhost"
cache_url: "redis.example.com"

worker:
database_url: "localhost"
cache_url: "redis.example.com"

对象的引用和合并

& 用来建立锚点(defaults),<< 表示合并到当前数据,***** 用来引用锚点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 定义一个配置对象的锚点
default_database: &db_defaults
driver: postgresql
port: 5432
pool_size: 10
timeout: 30
ssl: true

# 使用 << 合并操作符
development:
database:
<<: *db_defaults # 合并所有默认配置
host: localhost # 添加新的属性
name: dev_database # 添加新的属性

production:
database:
<<: *db_defaults # 再次使用相同的默认配置
host: prod.db.com # 添加不同的host
name: prod_database # 添加不同的name
ssl: false # 覆盖默认的ssl设置

解析结果

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
# 等价于以下完整配置
default_database:
driver: postgresql
port: 5432
pool_size: 10
timeout: 30
ssl: true

development:
database:
driver: postgresql # 来自 *db_defaults
port: 5432 # 来自 *db_defaults
pool_size: 10 # 来自 *db_defaults
timeout: 30 # 来自 *db_defaults
ssl: true # 来自 *db_defaults
host: localhost # 新增的属性
name: dev_database # 新增的属性

production:
database:
driver: postgresql # 来自 *db_defaults
port: 5432 # 来自 *db_defaults
pool_size: 10 # 来自 *db_defaults
timeout: 30 # 来自 *db_defaults
ssl: false # 覆盖了默认值true
host: prod.db.com # 新增的属性
name: prod_database # 新增的属性

数组的引用

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
# 定义数组锚点
common_dependencies: &common_deps
- lodash
- moment
- axios

basic_tools: &basic_tools
- webpack
- babel

# 直接引用整个数组
frontend_project:
dependencies: *common_deps
# 相当于:
# dependencies:
# - lodash
# - moment
# - axios

# 在数组中混合使用引用和新元素
backend_project:
dependencies:
- express # 新增的依赖
- *common_deps # 引用整个数组,会展开
- mongoose # 再新增的依赖

# 组合多个数组引用
fullstack_project:
dependencies:
- react
- *common_deps # 展开 common_deps
- *basic_tools # 展开 basic_tools
- typescript

相当于

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# backend_project 等价于:
backend_project:
dependencies:
- express
- lodash # 来自 *common_deps
- moment # 来自 *common_deps
- axios # 来自 *common_deps
- mongoose

# fullstack_project 等价于:
fullstack_project:
dependencies:
- react
- lodash # 来自 *common_deps
- moment # 来自 *common_deps
- axios # 来自 *common_deps
- webpack # 来自 *basic_tools
- babel # 来自 *basic_tools
- typescript

多个锚点的组合使用

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
# 定义多个不同用途的锚点
database_config: &db_config
driver: mysql
charset: utf8mb4
pool_min: 5
pool_max: 20

cache_config: &cache_config
provider: redis
ttl: 3600
max_memory: "512mb"

logging_config: &log_config
level: info
format: json
rotation: daily

# 组合使用多个锚点
microservices:
user_service:
<<: *db_config # 合并数据库配置
<<: *cache_config # 合并缓存配置
<<: *log_config # 合并日志配置
port: 8001 # 服务特有配置

order_service:
<<: *db_config # 复用数据库配置
<<: *log_config # 复用日志配置
port: 8002 # 不同的端口
# 注意:这里没有使用缓存配置

payment_service:
<<: *db_config # 复用数据库配置
<<: *cache_config # 复用缓存配置
port: 8003
# 覆盖日志级别
level: debug # 这会覆盖 log_config 中的 level

相当于

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
# user_service 等价于:
microservices:
user_service:
# 来自 db_config
driver: mysql
charset: utf8mb4
pool_min: 5
pool_max: 20
# 来自 cache_config
provider: redis
ttl: 3600
max_memory: "512mb"
# 来自 log_config
level: info
format: json
rotation: daily
# 服务特有
port: 8001

order_service:
# 来自 db_config
driver: mysql
charset: utf8mb4
pool_min: 5
pool_max: 20
# 来自 log_config
level: info
format: json
rotation: daily
# 服务特有
port: 8002

yaml 的多文档支持

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 第一个文档
---
name: Document 1
type: config
data:
key1: value1
key2: value2

# 第二个文档
---
name: Document 2
type: data
items:
- item1
- item2
- item3

# 文档结束标记(可选)
...

标签和类型转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 显式类型标签
integer_value: !!int "123"
float_value: !!float "456.789"
string_value: !!str 123
boolean_value: !!bool "yes"

# 自定义标签
custom_object: !MyClass
property1: value1
property2: value2

# 二进制数据
binary_data: !!binary |
R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5
OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+
+f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC

Spring Boot 管理配置文件

注入配置文件

yaml文件更强大的地方在于,他可以给我们的实体类直接注入匹配值,它允许开发者以声明式的方式配置应用程序

Spring Boot支持将YAML配置文件中的属性值自动注入到Java类中,主要通过以下几种方式实现:

使用@Value注解与配置文件占位符

@Value注解可以直接用于字段、方法参数和构造器参数,以注入配置文件中的值。它适用于简单的配置项。

1
2
3
4
5
6
7
server:
port: 8080

app:
name: MyApplication
version: 1.0.0
author: ergoutree

properties 也可以这样

1
2
3
4
server.port=8080
app.name=MyApplication
app.version=1.0.0
app.author=ergoutree

Java类示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Component
public class AppInfoService {

@Value("${app.name}")
private String appName;

@Value("${app.version}")
private String version;

@Value("${server.port}")
private int serverPort;

// 支持默认值,如果配置不存在则使用默认值
@Value("${app.description:这是一个Spring Boot应用}")
private String description;

public void printAppInfo() {
System.out.println("应用名称: " + appName);
System.out.println("版本: " + version);
System.out.println("端口: " + serverPort);
System.out.println("描述: " + description);
}
}
  • @Value("${app.name}") 会自动将YAML中的 app.name 值注入到 appName 字段
  • 支持嵌套属性,如 server.port
  • 可以设置默认值,语法是 ${key:defaultValue}

@ConfigurationProperties:

@ConfigurationProperties 是 Spring Boot 中的一个注解,用于将配置文件中的属性值绑定到 Java Bean 上。提供了一种更类型安全的方式来处理配置。这种方式适合注入一组相关的配置属性,更加优雅和类型安全。

通常,这个注解用于将外部属性文件中的属性值映射到应用程序的配置类中,以便在整个应用程序中方便地访问和使用这些属性。

这个注解通常与 @Configuration(声明是一个配置类) 或 @Component(声明是一个bean)一起使用,将配置属性绑定到一个特定的 Java Bean,然后以便让 Spring 容器识别并管理这个 Bean。

通过在类上使用 @ConfigurationProperties,可以将属性文件中的键与 Java Bean 的字段或属性进行映射。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
database:
host: localhost
port: 3306
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
connection-timeout: 30000
max-connections: 10

email:
smtp:
host: smtp.qq.com
port: 587
username: example@qq.com
password: your-password
from: noreply@example.com
templates:
welcome: welcome-template.html
reset-password: reset-password-template.html
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
@Component
@ConfigurationProperties(prefix = "database")
@Data // Lombok注解,自动生成getter/setter
public class DatabaseProperties {
private String host;
private int port;
private String username;
private String password;
private String driverClassName;
private long connectionTimeout;
private int maxConnections;
}

@Component // 声明为 Spring Bean
@ConfigurationProperties(prefix = "email")
@Data
public class EmailProperties {
private Smtp smtp = new Smtp();
private String from;
private Map<String, String> templates = new HashMap<>();

@Data
public static class Smtp {
private String host;
private int port;
private String username;
private String password;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Service
public class EmailService {

private final EmailProperties emailProperties;

public EmailService(EmailProperties emailProperties) {
this.emailProperties = emailProperties;
}

public void sendEmail() {
System.out.println("SMTP主机: " + emailProperties.getSmtp().getHost());
System.out.println("SMTP端口: " + emailProperties.getSmtp().getPort());
System.out.println("发件人: " + emailProperties.getFrom());
System.out.println("欢迎模板: " + emailProperties.getTemplates().get("welcome"));
}
}

说明:

  • @ConfigurationProperties(prefix = "database") 会将所有以 database 开头的配置映射到该类
  • 支持嵌套对象,如 email.smtp 会映射到 EmailProperties.Smtp
  • 支持集合类型,如 Map templates
  • 需要提供标准的getter/setter方法(可使用Lombok简化)

在Spring Boot的启动类或配置类上添加@EnableConfigurationProperties注解来启用@ConfigurationProperties支持

对于Spring Boot 2.2及以上版本,这一步通常是可选的,因为Spring Boot会自动配置

Environment方式

在Spring Boot中,Environment抽象是Spring框架提供的一个接口,它允许你访问配置属性以及应用程序的环境。虽然Environment不是一个专门用于注入配置的方式(像@Value@ConfigurationProperties那样),但它可以在某些情况下非常有用,特别是当你需要访问低级别的配置信息或进行条件化配置时。

Environment 接口提供了多种方法来获取配置属性,包括通过键名(key)直接访问和通过解析占位符(placeholders)来访问。

也就是说,读取环境变量,但是不仅仅读取环境变量它会从多个配置源读取配置,包括但不限于:

  • .环境变量(Operating System Environment Variables)
  • 系统属性(System Properties)
  • 配置文件
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
@Component
public class EnvironmentService {

private final Environment environment;

// 构造函数注入
public EnvironmentService(Environment environment) {
this.environment = environment;
}

// 或者使用@Autowired字段注入
// @Autowired
// private Environment environment;

public void demonstrateEnvironment() {
// 获取配置属性
String appName = environment.getProperty("app.name");
Integer serverPort = environment.getProperty("server.port", Integer.class);
String description = environment.getProperty("app.description", "默认描述");

System.out.println("应用名称: " + appName);
System.out.println("服务端口: " + serverPort);
System.out.println("应用描述: " + description);

// 获取当前激活的Profile
String[] activeProfiles = environment.getActiveProfiles();
System.out.println("激活的Profile: " + Arrays.toString(activeProfiles));

// 检查属性是否存在
if (environment.containsProperty("database.url")) {
System.out.println("数据库URL: " + environment.getProperty("database.url"));
}
}
}

Environment 接口的常用方法

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
@Service
public class ConfigurationService {

private final Environment env;

public ConfigurationService(Environment env) {
this.env = env;
}

public void demonstrateEnvironmentMethods() {
// 1. getProperty() - 获取属性值
String stringValue = env.getProperty("app.name");

// 2. getProperty(key, targetType) - 带类型转换
Integer intValue = env.getProperty("server.port", Integer.class);
Boolean boolValue = env.getProperty("app.debug", Boolean.class);

// 3. getProperty(key, defaultValue) - 带默认值
String valueWithDefault = env.getProperty("app.title", "默认标题");

// 4. getProperty(key, targetType, defaultValue) - 带类型转换和默认值
Integer portWithDefault = env.getProperty("custom.port", Integer.class, 9090);

// 5. getRequiredProperty() - 必需属性,不存在会抛异常
try {
String requiredValue = env.getRequiredProperty("app.name");
System.out.println("必需属性: " + requiredValue);
} catch (IllegalStateException e) {
System.out.println("必需属性不存在: " + e.getMessage());
}

// 6. containsProperty() - 检查属性是否存在
boolean hasProperty = env.containsProperty("app.version");

// 7. getActiveProfiles() - 获取激活的Profile
String[] activeProfiles = env.getActiveProfiles();

// 8. getDefaultProfiles() - 获取默认Profile
String[] defaultProfiles = env.getDefaultProfiles();

// 9. acceptsProfiles() - 检查Profile是否激活
boolean acceptsDev = env.acceptsProfiles("dev");
boolean acceptsDevOrTest = env.acceptsProfiles("dev", "test");

System.out.println("字符串值: " + stringValue);
System.out.println("整数值: " + intValue);
System.out.println("布尔值: " + boolValue);
System.out.println("带默认值: " + valueWithDefault);
System.out.println("端口带默认值: " + portWithDefault);
System.out.println("包含版本属性: " + hasProperty);
System.out.println("激活的Profile: " + Arrays.toString(activeProfiles));
System.out.println("默认Profile: " + Arrays.toString(defaultProfiles));
System.out.println("接受dev Profile: " + acceptsDev);
System.out.println("接受dev或test Profile: " + acceptsDevOrTest);
}
}
  • Environment提供了对配置属性的广泛访问,但它不是类型安全的。若需要类型安全的配置绑定,建议使用@ConfigurationProperties

  • Environment 还提供了对活动 profiles 的访问,这在进行条件化配置时非常有用。

  • 当你需要访问 Spring Boot 的 application.properties 或 application.yml 文件中的配置时,Environment 是一个很好的选择,但它也可以访问其他来源的配置,如命令行参数、JNDI、servlet上下文参数等。

@Configuration注解+@Bean (还可以加@Import引入三方)

先用@Configuration标记一个类作为配置类,相当于 XML 中的<beans>标签

  • 配置类会被 Spring 容器扫描并处理。
  • 配置类中的@Bean方法会被容器调用以创建和管理 Bean。
  • 配置类本身也是一个 Bean,可以被其他组件注入。

然后用@Bean标记其中的方法,返回一个对象并注册为 Spring 容器中的 Bean。

  • 方法名默认作为 Bean 的名称(可通过name属性指定其他名称)。
  • 可以指定 Bean 的作用域(如@Scope("prototype"))。
  • 可以通过@DependsOn指定依赖关系。

之后不同的地方就是,使用@Import导入其他配置类,将它们的 Bean 定义合并到当前配置中。

  • 引入第三方库的配置类。
  • 模块化配置,将相关配置分散到多个类中。
  • 导入没有使用@Configuration注解的普通类(会被当作配置类处理)。

这是基础配置类与 Bean 定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

@Bean
public MyService myService() {
return new MyServiceImpl(); // 创建并返回一个Bean
}

@Bean(name = "customDataSource")
public DataSource dataSource() {
// 配置并返回数据源
return DataSourceBuilder.create()
.url("jdbc:mysql://localhost:3306/mydb")
.username("root")
.password("password")
.build();
}
}

在这之上,我们就可以使用@Import引入其他配置类

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
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@Import({DatabaseConfig.class, SecurityConfig.class}) // 导入其他配置类
public class AppConfig {
// 可以继续定义其他Bean
}

// DatabaseConfig.java
@Configuration
public class DatabaseConfig {
@Bean
public DataSource dataSource() {
// 配置数据源
}
}

// SecurityConfig.java
@Configuration
public class SecurityConfig {
@Bean
public AuthenticationManager authenticationManager() {
// 配置认证管理器
}
}

使用 @PropertySources

@PropertySources@PropertySource的容器注解,用于同时指定多个配置文件源。

创建多个自定义配置文件

1
2
3
4
5
6
7
8
9
10
11
12
# custom.properties:
custom.app.name=自定义应用
custom.app.version=2.0.0
custom.database.driver=mysql
custom.cache.enabled=true
custom.cache.ttl=3600

# business.properties:
business.module.user=用户管理模块
business.module.order=订单处理模块
business.feature.payment=true
business.feature.notification=false

使用@PropertySource的配置类示例

基本用法,指定了使用 custom.properties

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
@Configuration
@PropertySource("classpath:custom.properties")
public class CustomConfiguration {

@Autowired
private Environment env;

@Bean
public CustomAppProperties customAppProperties() {
CustomAppProperties props = new CustomAppProperties();
props.setName(env.getProperty("custom.app.name"));
props.setVersion(env.getProperty("custom.app.version"));
props.setDatabaseDriver(env.getProperty("custom.database.driver"));
props.setCacheEnabled(env.getProperty("custom.cache.enabled", Boolean.class, false));
props.setCacheTtl(env.getProperty("custom.cache.ttl", Integer.class, 1800));
return props;
}
}

@Data
public class CustomAppProperties {
private String name;
private String version;
private String databaseDriver;
private boolean cacheEnabled;
private int cacheTtl;
}

@PropertySources多文件配置

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
@Configuration
@PropertySources({
@PropertySource("classpath:custom.properties"),
@PropertySource("classpath:business.properties"),
@PropertySource(value = "classpath:optional.properties", ignoreResourceNotFound = true),
@PropertySource(value = "file:${user.home}/app-config.properties", ignoreResourceNotFound = true)
})
public class MultiplePropertySourcesConfiguration {

@Autowired
private Environment env;

@Bean
public ApplicationSettings applicationSettings() {
ApplicationSettings settings = new ApplicationSettings();

// 从custom.properties读取
settings.setAppName(env.getProperty("custom.app.name"));
settings.setAppVersion(env.getProperty("custom.app.version"));
settings.setCacheEnabled(env.getProperty("custom.cache.enabled", Boolean.class));

// 从business.properties读取
settings.setUserModule(env.getProperty("business.module.user"));
settings.setOrderModule(env.getProperty("business.module.order"));
settings.setPaymentEnabled(env.getProperty("business.feature.payment", Boolean.class));

return settings;
}

@PostConstruct // 在 Bean 初始化后执行
public void printConfiguration() {
System.out.println("=== 配置信息 ===");
System.out.println("应用名称: " + env.getProperty("custom.app.name"));
System.out.println("应用版本: " + env.getProperty("custom.app.version"));
System.out.println("用户模块: " + env.getProperty("business.module.user"));
System.out.println("订单模块: " + env.getProperty("business.module.order"));
System.out.println("缓存启用: " + env.getProperty("custom.cache.enabled"));
System.out.println("支付功能: " + env.getProperty("business.feature.payment"));
}
}

@Data
public class ApplicationSettings {
private String appName;
private String appVersion;
private String userModule;
private String orderModule;
private Boolean cacheEnabled;
private Boolean paymentEnabled;
}
  • 加载多个配置文件:通过@PropertySources注解可以声明加载多个配置文件。
    • classpath: 前缀表示从类路径加载。
    • file: 前缀表示从文件系统加载,支持使用环境变量(如${user.home})。
  • 加载顺序:配置文件按声明顺序加载,后加载的文件会覆盖前面文件中同名的属性(例如,如果app-config.propertiescustom.properties都有custom.app.name属性,将使用app-config.properties中的值)。
  • 可选文件:通过ignoreResourceNotFound = true指定可选文件,如果文件不存在则忽略(如optional.properties和用户主目录下的配置文件)。

条件化属性源

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
@Configuration
public class ConditionalPropertySourceConfiguration {

@Configuration
@Profile("dev")
@PropertySources({
@PropertySource("classpath:dev.properties"),
@PropertySource("classpath:dev-database.properties")
})
static class DevConfiguration {
}

@Configuration
@Profile("prod")
@PropertySources({
@PropertySource("classpath:prod.properties"),
@PropertySource("classpath:prod-database.properties")
})
static class ProdConfiguration {
}

@Configuration
@Profile("test")
@PropertySource("classpath:test.properties")
static class TestConfiguration {
}
}
  • @Profile 注解:根据激活的环境(如devprodtest)选择性加载配置类。
  • @PropertySource 注解:然后加载特定环境的配置文件。实现环境配置分离

使用 @ImportResource 加载 XML 配置文件

如果你有传统的 Spring XML 配置文件,需要在 Spring Boot 中加载。

1
2
3
4
5
6
7
8
9
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="myService" class="com.example.MyService">
<property name="message" value="Hello from XML!"/>
</bean>
</beans>
1
2
3
4
5
6
7
8
9
10
11
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;

@SpringBootApplication
@ImportResource("classpath:applicationContext.xml") // 加载 XML 配置
public class MyApp {
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
}
}

Spring Boot 中的 application.properties 与 application.yaml

其实SpringBoot的配置文件有.properties和.yml两种形式,两种配置文件的效果类似,只不过是格式不同而已

SpringBoot使用一个全局的配置文件 , 配置文件名称是固定的

  • application.properties
    • 语法:使用 key=value 格式,用 . 分隔层级。
    • 简单直接,适合简单配置。
    • 支持占位符引用(如 ${app.name})。
    • 不支持复杂的嵌套结构(需重复前缀)。
  • application.yaml / application.yml
    • 语法:使用 key: value 格式,通过缩进表示层级。
    • 结构清晰,适合复杂配置(减少重复前缀)。
    • 支持列表、对象嵌套。
    • 更易读,尤其对于多层次配置。

注意:

  • 当properties和yml同时存在时,properties的优先级会比yml高

  • 两种文件都是用#注释,yml 的优点在于可以省去一下重复代码

  • 在properties文件里面的. 连接在yml文件里面全部换成 进行连接,并且每一级之间必须换行,在第二级开始应该进行几个空格的缩进,如果是同级的就不需要进行缩进

properties 文件和 yml 文件优先级:

Spring Boot 对配置文件的加载遵循特定的顺序和优先级,总结如下:

单文件优先级

  • 相同类型文件

    • 高优先级位置的文件覆盖低优先级位置的文件。

    • 优先级从高到低:

      1. 命令行参数(如 --server.port=8081)。

        • ```bash java -jar app.jar –server.port=8081 –spring.profiles.active=prod

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12

          - 直接在启动应用时传入的参数

          - 格式:`--属性名=值`

          - 优先级最高,会覆盖所有其他配置

          2. 环境变量(如 `SERVER_PORT=8081`)。

          ```bash
          export SERVER_PORT=8081
          export SPRING_PROFILES_ACTIVE=prod

        • 操作系统级别的环境变量

        • Spring Boot会自动将环境变量转换为配置属性

        • 命名规则:大写字母,用下划线分隔

      2. SPRING_APPLICATION_JSON 环境变量(JSON 格式配置)。

        1
        export SPRING_APPLICATION_JSON='{"server.port":8081,"logging.level.root":"DEBUG"}'
        • 特殊的环境变量,值为JSON格式
      • 可以一次性设置多个配置项
        • 比普通环境变量优先级稍低
      1. java:comp/env JNDI 属性。

        1
        2
        3
         // 在Java EE容器中通过JNDI设置
        Context ctx = new InitialContext();
        ctx.bind("java:comp/env/server.port", "8081");
        • 主要用于Java EE环境
        • 通过JNDI(Java命名和目录接口)设置的属性
      2. Java 系统属性(System.getProperties())。

        1
        java -Dserver.port=8081 -jar app.jar
        • 通过-D参数设置的JVM系统属性
        • 也可以在代码中通过System.setProperty()设置
      3. RandomValuePropertySource(随机值,如 random.*)。

        1
        2
        3
        4
        # 在配置文件中使用随机值
        my.secret=${random.value}
        my.number=${random.int}
        my.uuid=${random.uuid}
        • Spring Boot内置的随机值生成器
        • 用于生成随机配置值
      4. 应用外部的 application-{profile}.properties.yaml(如 config/application.properties)。

        1
        2
        3
        4
        5
        project/
        ├── app.jar
        ├── config/
        │ ├── application-dev.properties # 外部Profile配置
        │ └── application-prod.properties
        • 位于jar包外部的带环境标识的配置文件
        • 通常放在config/目录下
        • 优先级高于应用内部的同类文件
      5. 应用内部的 application-{profile}.properties.yaml(如 src/main/resources/application-dev.properties)。

        1
        2
        3
        4
        src/main/resources/
        ├── application.properties
        ├── application-dev.properties # 内部Profile配置
        └── application-prod.properties
        • 打包在jar内的带环境标识的配置文件
        • 位于src/main/resources/目录
      6. 应用外部的 application.properties.yaml

        1
        2
        3
        4
        project/
        ├── app.jar
        ├── config/
        │ └── application.properties # 外部通用配置
        • jar包外部的通用配置文件
        • 不带profile后缀的配置文件
      7. 应用内部的 application.properties.yaml(最低优先级)。

        1
        2
        src/main/resources/
        └── application.properties # 内部通用配置,优先级最低
        • 打包在jar内的通用配置文件
        • 优先级最低,最容易被其他配置覆盖

同路径下 .properties.yaml 的优先级

  • application.propertiesapplication.yaml 同时存在于同一位置时
    • .properties 文件优先级更高,会覆盖 .yaml 中的同名属性。
    • 示例
      • application.properties 包含 server.port=8080,而 application.yaml 包含 server.port=8081,则最终端口为 8080

不同路径下的配置文件

  • 外部配置 > 内部配置
    • 位于应用外部(如 config/ 目录)的配置文件优先级高于应用内部(src/main/resources)的配置文件。

特定环境配置(application-{profile}.properties

  • 特定环境配置 > 通用配置
    • 激活的环境配置(如 application-dev.properties)会覆盖通用配置(application.properties)。
    • 若同时存在 application-dev.propertiesapplication-dev.yaml,则 .properties 仍优先。
场景 优先级顺序(高 → 低)
同类型文件 外部 > 内部,特定环境 > 通用环境
同路径下 .properties vs .yaml .properties > .yaml
特定环境配置 application-{profile}.properties > application.properties