一、为什么特殊符号会导致问题?
spring 在注入值时,会经过多层解析:
- xml 解析:
<、>、&等是 xml 语法字符 - 属性占位符解析:
${}是占位符语法,:是默认值分隔符 - spel 表达式解析:
#{}是表达式语法 - 类型转换:
,可能被当作集合分隔符 - yaml 解析:
:、#、{、}等是 yaml 语法字符
每一层都可能把特殊符号“吃掉”或误解析,导致注入结果与预期不符。
二、xml 配置中的特殊符号
2.1 xml 转义字符
xml 中有五个预定义实体,必须转义:
| 特殊符号 | 转义写法 | 说明 |
|---|---|---|
< | < | 小于号 |
> | > | 大于号 |
& | & | 和号 |
" | " | 双引号 |
' | ' | 单引号 |
<!-- ❌ 错误:& 未转义 --> <property name="url" value="jdbc:mysql://localhost:3306/db?usessl=false&servertimezone=utc"/> <!-- ✅ 正确:& 转义为 & --> <property name="url" value="jdbc:mysql://localhost:3306/db?usessl=false&servertimezone=utc"/>
2.2 使用 cdata 避免转义
如果值中包含大量特殊字符,用 <![cdata[...]]> 包裹:
<property name="sql">
<value><![cdata[
select * from user where age > 18 and name != 'test'
]]></value>
</property>cdata 中的内容不会被 xml 解析器解析,>、<、& 都可以直接写。
2.3 值中含双引号或单引号
<!-- 值中含双引号:外层用单引号 --> <property name="message" value='he said "hello"'/> <!-- 值中含单引号:外层用双引号 --> <property name="message" value="it's a test"/>
三、@value 中的特殊符号
3.1 值中含$符号
$ 在 @value 中用于 ${} 占位符。如果值本身包含 $,需要转义。
// 配置文件:app.price=$100
// ❌ 错误:spring 会把 $100 当作占位符
@value("${app.price}")
// ✅ 正确:配置文件中转义,或用默认值
在 properties 文件中:
app.price=$100
在 @value 中,$ 本身不需要转义,但如果配置文件中写 ${app.price} 这种形式,$ 会被解析:
// 如果配置值就是字面量 "$100"
@value("${app.price}")
private string price; // 注入 "$100",正常
如果值中包含 ${ 这样的组合,需要用 \ 转义:
@value("${app.template:\\${name}}")
private string template; // 注入 "${name}"
3.2 值中含#符号
# 在 @value 中用于 #{} spel 表达式。如果值本身包含 #,需要处理。
app: color: "#ff0000"
// ❌ 错误:spring 会把 #ff0000 当作 spel
@value("${app.color}")
// ✅ 正确:配置值中的 # 本身不会触发 spel,因为 # 只在 #{} 中才触发
@value("${app.color}")
private string color; // 注入 "#ff0000",正常
只有当 # 后面紧跟 { 时才会触发 spel 解析。单独的 # 没问题。
3.3 值中含:符号
: 在 @value 中用于分隔配置键和默认值。如果配置值本身包含 :,需要注意。
// 配置文件:app.url=jdbc:mysql://localhost:3306/db
// ✅ 正常,因为 : 在配置值中,不在占位符语法中
@value("${app.url}")
private string url; // 注入 "jdbc:mysql://localhost:3306/db"
但如果写默认值时:
// ❌ 错误:spring 会把 jdbc 当作默认值,后面的内容被忽略
@value("${app.url:jdbc:mysql://localhost:3306/db}")
private string url;
// ✅ 正确:用 spel 或拆分配置
@value("${app.url:}")
private string url;
3.4 值中含逗号,
逗号在集合注入中会被当作分隔符。
app: servers: server1,server2,server3
@value("${app.servers}")
private list<string> servers; // 自动拆分为 3 个元素
如果值本身包含逗号:
app: message: "hello, world"
// ❌ 错误:会被拆分成 "hello" 和 " world" 两个元素
@value("${app.message}")
private list<string> messages;
// ✅ 正确:用 string 接收
@value("${app.message}")
private string message;
3.5 值中含反斜杠\
app: path: "c:\\users\\admin"
@value("${app.path}")
private string path; // 注入 "c:\users\admin"
在 yaml 中,双引号内的 \ 是转义字符,需要写 \\。单引号内不转义:
app: path: 'c:\users\admin' # 单引号内原样输出
四、yaml 中的特殊符号
4.1 :的处理
yaml 中 : 是键值分隔符。如果值中含 :,需要加引号。
# ❌ 错误:值中的 : 可能被解析为新的键值对 app: url: jdbc:mysql://localhost:3306/db # ✅ 正确:加引号 app: url: "jdbc:mysql://localhost:3306/db"
4.2 #的处理
# 在 yaml 中是注释符。如果值中含 #,需要加引号。
# ❌ 错误:# 后面被当作注释 app: color: #ff0000 # ✅ 正确:加引号 app: color: "#ff0000"
4.3 {和}的处理
{} 在 yaml 中表示内联 map。如果值中含 {},需要加引号。
# ❌ 错误:被解析为 map
app:
template: {name}
# ✅ 正确:加引号
app:
template: "{name}"4.4 [和]的处理
[] 在 yaml 中表示列表。如果值中含 [],需要加引号。
# ❌ 错误:被解析为列表 app: pattern: [a-z]+ # ✅ 正确:加引号 app: pattern: "[a-z]+"
4.5 &*!|>的处理
这些都是 yaml 的特殊字符,值中含这些字符时建议加引号。
app: expr: "a & b" alias: "*ref" tag: "!important" multi: "|line" folded: ">text"
五、properties 文件中的特殊符号
5.1 \转义
properties 文件中,\ 是转义字符:
# \ 需要写成 \\ app.path=c:\\users\\admin # : 和 = 在键中需要转义 app\:key=value # 空格在值末尾需要转义 app.name=hello\
5.2 unicode 转义
properties 文件默认使用 iso-8859-1 编码,中文需要转义:
app.name=\u5f20\u4e09
或者使用 utf-8 编码加载(spring boot 默认支持)。
5.3 $的处理
app.price=$100
在 @value("${app.price}") 中,$100 会正常注入,因为 $ 单独出现不会触发占位符解析。
六、spel 表达式中的特殊符号
6.1 字符串中的单引号
spel 中字符串用单引号表示。如果字符串本身含单引号,需要转义:
@value("#{'it\\'s a test'}")
private string message; // "it's a test"
6.2 字符串中的双引号
@value("#{'he said \"hello\"'}")
private string message;
6.3 值的拼接
@value("#{'${app.name}' + '-' + '${app.version}'}")
private string fullname;
七、完整示例:数据库连接 url
数据库 url 是典型的含特殊符号的配置,包含 :、/、?、&、= 等。
yaml 写法:
spring:
datasource:
url: "jdbc:mysql://localhost:3306/mydb?usessl=false&servertimezone=asia/shanghai&characterencoding=utf8"
username: root
password: "p@ssw0rd#123"- url 加引号,避免
&和:被误解析 - 密码含
#,必须加引号 - 密码含
@,yaml 中@不是特殊字符,但加引号更安全
properties 写法:
spring.datasource.url=jdbc:mysql://localhost:3306/mydb?usessl=false&servertimezone=asia/shanghai&characterencoding=utf8 spring.datasource.username=root spring.datasource.password=p@ssw0rd#123
properties 文件中 &、#、@ 不需要转义(# 在行首才是注释)。
xml 写法:
<bean id="datasource" class="com.zaxxer.hikari.hikaridatasource">
<property name="jdbcurl"
value="jdbc:mysql://localhost:3306/mydb?usessl=false&servertimezone=asia/shanghai&characterencoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="p@ssw0rd#123"/>
</bean>xml 中 & 必须转义为 &。
八、各场景特殊符号处理速查表
| 特殊符号 | xml | yaml | properties | @value | spel |
|---|---|---|---|---|---|
& | & | 建议加引号 | 直接写 | 直接写 | 直接写 |
< | < | 建议加引号 | 直接写 | 直接写 | 直接写 |
> | > | 建议加引号 | 直接写 | 直接写 | 直接写 |
" | " 或外层用单引号 | 外层用单引号 | 直接写 | 直接写 | \" |
' | ' 或外层用双引号 | 外层用双引号 | 直接写 | 直接写 | \' |
: | 直接写 | 建议加引号 | 键中需转义 | 默认值分隔符 | 直接写 |
# | 直接写 | 必须加引号 | 行首是注释 | 直接写 | #{} 触发表达式 |
$ | 直接写 | 直接写 | 直接写 | ${} 触发占位符 | 直接写 |
{ } | 直接写 | 必须加引号 | 直接写 | ${} 语法 | #{} 语法 |
[ ] | 直接写 | 必须加引号 | 直接写 | 直接写 | 直接写 |
, | 直接写 | 直接写 | 直接写 | 集合分隔符 | 直接写 |
\ | 直接写 | 双引号内 \\ | \\ | 转义符 | \\ |
% | 直接写 | 直接写 | 直接写 | 直接写 | 取模运算符 |
九、最佳实践
yaml 中值一律加引号。 这是最省心的做法。加引号后,yaml 不再解析值中的特殊字符,原样传递。
app:
url: "jdbc:mysql://localhost:3306/db?usessl=false&servertimezone=utc"
password: "p@ssw0rd#123"
pattern: "[a-z]+"
template: "{name}"xml 中注意五个预定义实体。 &、<、>、"、' 必须转义,或用 cdata 包裹。
@value 中的默认值含 : 时,用 spel 处理。 因为 : 是默认值分隔符,直接写会有歧义。
// ❌ 有歧义
@value("${app.url:jdbc:mysql://localhost/db}")
// ✅ 用 spel
@value("#{'${app.url:}' ?: 'jdbc:mysql://localhost/db'}")
密码等敏感值始终加引号。 密码中可能包含 #、@、$、! 等字符,加引号可以避免解析问题。
配置值中的逗号用 string 接收。 如果需要保持逗号不被拆分,用 string 类型接收,再手动处理。
十、总结
| 场景 | 核心问题 | 解决方案 |
|---|---|---|
xml 中的 &、<、> | xml 语法冲突 | 转义为 &、<、> 或用 cdata |
yaml 中的 :、#、{}、[] | yaml 语法冲突 | 值加引号 |
properties 中的 \ | 转义字符 | 写 \\ |
@value 默认值中的 : | 分隔符冲突 | 用 spel 表达式处理 |
@value 中的 $、# | 占位符/spel 语法 | 单独出现不触发,${ 和 #{ 需转义 |
集合注入中的 , | 被当作分隔符 | 改用 string 接收后手动拆分 |
| spel 中的引号 | 字符串边界冲突 | 用 \' 或 \" 转义 |
处理特殊符号的核心原则是:知道每一层解析器的语法规则,在对应层级做正确的转义或加引号。yaml 中加引号是最简单有效的做法;xml 中注意预定义实体和 cdata;@value 中注意 ${}、#{} 和 : 的语法含义。理解这些规则,就能避免绝大多数注入异常。
以上就是spring注入值中含有特殊符号的处理详解的详细内容,更多关于spring注入值中含有特殊符号的资料请关注代码网其它相关文章!
发表评论