数组构造 (Array Construction)
你可以使用熟悉的 Java 语法来构建数组,也可以选择性地提供初始化器,以便在构造时填充数组。如下例所示:
java
int[] numbers1 = (int[]) parser.parseExpression("new int[4]").getValue(context);
// 带有初始化器的数组
int[] numbers2 = (int[]) parser.parseExpression("new int[] {1, 2, 3}").getValue(context);
// 多维数组
int[][] numbers3 = (int[][]) parser.parseExpression("new int[4][5]").getValue(context);kotlin
val numbers1 = parser.parseExpression("new int[4]").getValue(context) as IntArray
// 带有初始化器的数组
val numbers2 = parser.parseExpression("new int[] {1, 2, 3}").getValue(context) as IntArray
// 多维数组
val numbers3 = parser.parseExpression("new int[4][5]").getValue(context) as Array<IntArray>注意
目前在构造多维数组时,不支持提供初始化器。
警告
任何构造数组的表达式(例如通过 new int[4] 或 new int[] {1, 2, 3})都 无法被编译 为字节码。详见编译器限制。
补充教学
1. 数组构造 vs 内联列表
虽然 SpEL 支持数组构造,但在大多数情况下,你会发现使用 内联列表 ({}) 更加方便:
- 语法简洁:
{1, 2, 3}比new int[] {1, 2, 3}简短得多。 - 动态类型:内联列表会自动根据环境推断类型(通常是
ArrayList),而数组构造则强绑定了 Java 的原生数组类型。 - 性能优选:正如之前的章节所说,内联列表如果是静态字面量会被缓存为单例常量。而数组构造(
new关键字)每次执行表达式都会分配新的内存空间。
2. 什么时候必须用数组构造?
尽管有性能和易用性的差异,但在某些特定场景下,数组构造是不可替代的:
- 严格的方法签名:如果你在表达式中调用一个 Java 方法,而该方法明确要求传入
String[]而不是List<String>,那么你就需要用new String[] {'a'}。 - 固定长度需求:如果你需要一个预分配大小且后续会填充值的容器(通过
setValue),new int[10]是最直接的方式。
3. 理解“不可编译”的含义
文档中提到的“警告”非常重要。如果你的 Spring 项目开启了 SpEL 编译模式(SpelCompilerMode.IMMEDIATE 或 MIXED),对于大部分表达式,Spring 会把它们转换成原生的 Java 字节码以获得极高性能。 但是,一旦表达式中包含 new 一个数组的操作,这部分逻辑就只能退回到“解释执行”模式。
- 性能影响:如果这个表达式位于一个高频调用的拦截器或循环中,它会比已编译的表达式慢几个数量级。
- 架构建议:避免在性能敏感的热点代码中使用 SpEL 动态创建数组。尽量提前在 Java 代码中准备好数组,或者直接使用内联列表。