本文探讨了YAML文档结构对Spring Boot应用程序配置加载的影响。特别关注了文档分隔符(---
)、特定环境配置与配置属性解析之间的交互关系。研究表明,配置属性相对于文档分隔符的不当放置可能导致意外的应用程序行为,本文以JWT配置为例进行了具体分析。
Spring Boot提供了强大的配置机制,支持多种格式,包括YAML。YAML的层次结构便于组织配置管理,特别是对于跨多个环境部署的应用程序。然而,YAML的文档分隔符语法与Spring Boot的特定环境配置加载机制之间的微妙交互可能导致不易察觉的问题。
本研究论文探讨了这种交互的具体表现:属性放置位置相对于文档分隔符对Spring Boot应用程序配置加载行为的影响。
在YAML中,三个连字符的分隔符(---
)用于在单个文件中划分不同的文档。Spring Boot利用这一特性在统一的配置文件中定义特定环境的配置。
Spring Boot允许通过spring.config.activate.on-profile
属性(在Spring Boot 2.4.0及以后版本中)或spring.profiles
属性(在早期版本中)进行特定环境配置。当某个环境被激活时,Spring Boot会在默认配置的基础上应用相应的特定环境配置。
Spring Boot按以下顺序加载配置属性:
在每个类别中,属性按照其来源的优先级顺序加载。
Spring Boot中YAML配置的一个关键方面是属性如何与文档关联。**在文档分隔符之后定义的属性与该分隔符引入的文档相关联。**如果某个文档通过spring.config.activate.on-profile
指定了一个环境,则该文档中的所有属性都被视为该环境特有的配置。
考虑一个在application.yml
中配置了JWT认证的Spring Boot应用程序。以下配置结构导致在使用dev
环境运行时无法访问JWT属性:
# 默认配置
server:
port: 8080
servlet:
context-path: /api
spring:
application:
name: rbac-admin
profiles:
active: dev
# ... 其他默认配置 ...
---
# 开发环境配置
spring:
config:
activate:
on-profile: dev
springdoc:
api-docs:
enabled: true
---
# 测试环境配置
spring:
config:
activate:
on-profile: test
# ... 测试环境特有属性 ...
---
# 生产环境配置
spring:
config:
activate:
on-profile: prod
springdoc:
api-docs:
enabled: false
# JWT配置(放在最后一个文档分隔符之后)
jwt:
secret: secret_key_1234567890_abcdefg_123456_09898787
access-token-expiration: 1800000
refresh-token-expiration: 604800000
token-prefix: "Bearer "
header-string: "Authorization"
issuer: tttt
在上述配置中,JWT属性放置在最后一个文档分隔符之后,该分隔符引入了生产环境配置。因此,这些JWT属性隐式地与生产环境(prod
)相关联。当应用程序以dev
环境运行时,这些属性不会被加载。
将JWT属性移至默认配置部分(任何文档分隔符之前)可确保无论激活哪个环境,这些属性都会被加载:
# 默认配置
server:
port: 8080
servlet:
context-path: /api
# JWT配置(放在默认配置中)
jwt:
secret: secret_key_1234567890_abcdefg_123456_parade
access-token-expiration: 1800000
refresh-token-expiration: 604800000
token-prefix: "Bearer "
header-string: "Authorization"
issuer: parade
spring:
application:
name: rbac-admin
profiles:
active: dev
# ... 其他默认配置 ...
---
# 开发环境配置
spring:
config:
activate:
on-profile: dev
# ... 开发环境特有属性 ...
另一种方法是,如果需要环境特定的值,可以在每个环境特定部分中复制JWT属性:
# 默认配置
server:
port: 8080
# ... 其他默认属性 ...
---
# 开发环境配置
spring:
config:
activate:
on-profile: dev
# ... 开发环境特有属性 ...
# 开发环境的JWT配置
jwt:
secret: dev_secret_key
access-token-expiration: 1800000
# ... 其他JWT属性 ...
---
# 生产环境配置
spring:
config:
activate:
on-profile: prod
# ... 生产环境特有属性 ...
# 生产环境的JWT配置
jwt:
secret: prod_secret_key
access-token-expiration: 3600000
# ... 其他JWT属性 ...
为了更好地组织,可以将JWT配置提取到单独的文件中:
# application-jwt.yml
jwt:
secret: secret_key_1234567890_abcdefg_123456_parade
access-token-expiration: 1800000
refresh-token-expiration: 604800000
token-prefix: "Bearer "
header-string: "Authorization"
issuer: parade
然后在主配置中包含这个文件:
# application.yml
spring:
profiles:
active: dev
#include多个使用,分隔
include: jwt
对假设的行为进行了实证验证:
以下是Spring Boot应用程序YAML配置文件的推荐结构:
# 默认配置(适用于所有环境)
common-property1: value1
common-property2: value2
spring:
application:
name: application-name
# ... 其他通用属性 ...
---
# 开发环境配置
spring:
config:
activate:
on-profile: dev
dev-specific-property: value
---
# 测试环境配置
spring:
config:
activate:
on-profile: test
test-specific-property: value
---
# 生产环境配置
spring:
config:
activate:
on-profile: prod
prod-specific-property: value