内联 Map (Inline Maps)
你也可以通过使用 {key:value} 标记法在表达式中直接表示 Map。如下例所示:
java
// 评估结果为一个包含两个条目的 Java Map
Map inventorInfo = (Map) parser.parseExpression("{name:'Nikola',dob:'10-July-1856'}").getValue(context);
// 嵌套 Map
Map mapOfMaps = (Map) parser.parseExpression("{name:{first:'Nikola',last:'Tesla'},dob:{day:10,month:'July',year:1856}}").getValue(context);kotlin
// 评估结果为一个包含两个条目的 Java Map
val inventorInfo = parser.parseExpression("{name:'Nikola',dob:'10-July-1856'}").getValue(context) as Map<*, *>
// 嵌套 Map
val mapOfMaps = parser.parseExpression("{name:{first:'Nikola',last:'Tesla'},dob:{day:10,month:'July',year:1856}}").getValue(context) as Map<*, *>单独的 {:} 表示一个空 Map。出于性能原因,如果 Map 本身完全由固定字面量或其他嵌套的常量结构(列表或 Map)组成,则会创建一个常量 Map 来表示该表达式(而不是在每次评估时都构建一个新 Map)。Map 的键通常不需要加引号(除非键包含点号 .)。上面的示例中都没有对键使用引号。
补充教学
1. 空集合的坑:{} vs {:}
在许多脚本语言(如 JavaScript)中,{} 代表空对象/Map。但在 SpEL 中,规则不同:
{}:是 空列表 (Empty List)。{:}:是 空 Map (Empty Map)。 在使用时请务必注意这个冒号,它是区分列表和映射的关键。
2. 键名引号的逻辑
SpEL 允许在大多数情况下省略键名的引号,这使得表达式更像是一个简单的配置文件。
- 推荐做法:简单的字母数字键名不加引号,如
{name: 'Tesla'}。 - 强制引号:如果键名包含特殊字符(如点号、空格),或者你想用变量作为键名的一部分(虽然通常不推荐这样做),则必须加引号:
{'user.name': 'Tesla'}。
3. 实战场景:初始化复杂配置
内联 Map 非常适合在 @Value 注解中初始化具有默认值的映射:
java
@Value("#{ {'priority': 'high', 'retries': 3} }")
private Map<String, Object> defaultSettings;这种方式比在代码中手动 put 数据要整洁得多,尤其是在定义一些常量查找表时。
4. 性能优化提示
正如官方文档所述,Spring 会在解析阶段通过“深度解析”来判断一个内联 Map 是否是静态的。如果你的 Map 定义中所有键和值都是字面量,它就会被转为一个单例常量。 关键点:尽量避免在内联 Map 中混入非静态的表达式(如调用方法获取值),因为一旦混入,每次执行 SpEL 都会重新创建一个临时的 Map 对象。