前言
在Spring Boot项目中,YAML配置文件常使用占位符(如 @logging.level@
)实现动态配置。然而,若未正确配置Maven资源过滤机制,占位符可能未被替换,导致ScannerException
。
一、问题现象与报错分析
1.1 报错信息
ERROR org.springframework.boot.SpringApplication -- Application run failed
org.yaml.snakeyaml.scanner.ScannerException: while scanning for the next token
found character '@' that cannot start any token. (Do not use @ for indentation)
in 'reader', line 14, column 19:
com.xiaoli: @logging.level@
^
1.2 错误定位
- 错误类型:
ScannerException
表明YAML解析器在读取配置文件时遇到非法字符。 - 关键线索:
- 错误位置指向
@logging.level@
,说明占位符未被替换。 Do not use @ for indentation
提示YAML语法中@
不可作为标识符开头。
- 错误位置指向
- 根本原因:
- Maven构建时未正确替换YAML文件中的占位符,导致残留的
@...@
符号被YAML解析器识别为非法字符。
- Maven构建时未正确替换YAML文件中的占位符,导致残留的
二、解决方案与配置解析
2.1 核心思路
通过 Maven资源过滤机制,在构建过程中动态替换YAML文件中的占位符(如${logging.level}
),确保最终输出的配置文件不含未解析的占位符。
2.2 配置详解
在pom.xml
中,通过<resources>
标签配置资源过滤规则:
<build>
<resources>
<!-- 第一个resource:复制所有资源,但关闭过滤 -->
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
</resource>
<!-- 第二个resource:仅对指定文件启用过滤 -->
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>application*</include>
<include>bootstrap*</include>
<include>banner*</include>
</includes>
</resource>
</resources>
</build>
2.2.1 配置作用解析
第一个
<resource>
块:<directory>
:指定资源目录为src/main/resources
。<filtering>false</filtering>
:关闭过滤功能,确保所有文件(包括未使用占位符的文件)被直接复制到构建输出目录(如target/classes
)。- 作用:避免因过滤导致某些资源文件被遗漏。
第二个
<resource>
块:<directory>
:仍指向src/main/resources
,但通过<includes>
限制范围。<includes>
:仅包含以application
、bootstrap
、banner
开头的文件(如application.yml
、bootstrap.properties
)。<filtering>true</filtering>
:启用过滤功能,替换文件中的占位符(如${logging.level}
)。- 作用:对目标文件进行占位符替换,同时避免对其他文件重复操作。
2.2.2 关键点说明
- 资源覆盖机制:
Maven会按顺序处理资源块:- 第一个块将所有文件(未过滤)复制到输出目录。
- 第二个块再次复制指定文件(已过滤),覆盖未过滤的版本,确保最终文件为已替换的版本。
- 占位符格式:
Maven默认使用${...}
格式的占位符,而非@...@
。若需使用其他符号(如@...@
),需通过<configuration>
自定义占位符语法(复杂度较高,建议采用标准格式)。
三、扩展知识点与最佳实践
3.1 YAML语法规范
- 除了我们这种动态替换的场景,不要用
@
符号:- YAML中
@
不可作为标识符开头,其常用于注释或特殊语法(如@start
)。 - 示例错误:
key: @value@ # 非法,YAML解析器无法识别
- YAML中
- 正确缩进与冒号:
- 使用2个空格缩进,避免制表符(Tab)。
- 键值对后需加空格,如
key: value
而非key:value
。
3.2 Maven资源过滤机制
- 过滤流程:
- Maven读取
pom.xml
中的<properties>
或<profiles>
定义的属性值。 - 在构建时,替换资源文件中
${属性名}
为实际值。
- Maven读取
- 属性定义示例:
<properties> <logging.level>debug</logging.level> </properties>
- 环境配置:
<profiles> <profile> <id>dev</id> <properties> <logging.level>debug</logging.level> </properties> </profile> <profile> <id>prod</id> <properties> <logging.level>info</logging.level> </properties> </profile> </profiles>
3.3 验证配置有效性
- 清理并构建:
mvn clean install -Pdev # 指定dev环境
- 检查输出文件:
- 进入
target/classes
目录,确认application.yml
中的占位符已被替换为实际值(如debug
)。
- 进入
- 日志验证:
- 启动应用后,查看日志是否包含
logging.level
的生效信息。
- 启动应用后,查看日志是否包含
四、常见问题与排查建议
4.1 问题:占位符未被替换
- 原因:
- 未在
pom.xml
中定义属性值。 - 文件未被包含在
<includes>
中。
- 未在
- 解决:
- 检查
<properties>
或<profiles>
中是否定义了对应属性。 - 确保文件名匹配
<include>
规则(如application.yml
需匹配application*
)。
- 检查
4.2 问题:资源文件缺失
- 原因:
- 第一个
<resource>
块未关闭过滤,导致部分文件未被复制。
- 第一个
- 解决:
- 确保第一个
<resource>
块的<filtering>
设置为false
。
- 确保第一个
五、总结
5.1 核心知识点回顾
- Maven资源过滤:通过
<resources>
配置动态替换占位符。 - YAML语法规范:避免使用
@
符号,严格遵循缩进与格式。 - 环境配置管理:通过
<profiles>
实现多环境属性切换。
5.2 知识点延伸
- 进阶配置:
- 使用
<excludes>
排除特定文件。 - 通过
<filter>
指定额外的过滤属性文件。
- 使用
- 工具辅助:
- 使用IDEA的Maven插件实时检查资源过滤配置。
- 使用
yamllint
工具验证YAML文件格式。
六、完整配置示例
<project>
<!-- 其他配置省略 -->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>application*.yml</include>
<include>bootstrap*.properties</include>
</includes>
</resource>
</resources>
<properties>
<logging.level>debug</logging.level>
</properties>
<profiles>
<profile>
<id>prod</id>
<properties>
<logging.level>info</logging.level>
</properties>
</profile>
</profiles>
</build>
</project>