Properties, Arrays, Lists, Maps, and Indexers
Spring 表达式语言支持在对象图中导航并索引到各种结构中。
数值索引值从零开始,例如在 Java 中访问数组中的第 nth 个元素时。 |
有关如何使用空安全运算符浏览对象图,并对各种结构进行索引的详细信息,请参阅 Safe Navigation Operator 部分。 |
Property Navigation
您可以通过使用句点指示嵌套属性值来导航对象图中的属性引用。Inventor
类、pupin
和 tesla
的实例已使用已在 Classes used in the examples 部分中列出的数据填充。通过导航 down 对象图并获取特斯拉的出生年份和普平的出生城市,我们使用以下表达式:
-
Java
-
Kotlin
// evaluates to 1856
int year = (Integer) parser.parseExpression("birthdate.year + 1900").getValue(context);
// evaluates to "Smiljan"
String city = (String) parser.parseExpression("placeOfBirth.city").getValue(context);
// evaluates to 1856
val year = parser.parseExpression("birthdate.year + 1900").getValue(context) as Int
// evaluates to "Smiljan"
val city = parser.parseExpression("placeOfBirth.city").getValue(context) as String
属性名称的首字母可以不区分大小写。 因此,上面的示例中的表达式可以分别写成 |
Indexing into Arrays and Collections
数组或集合(例如,Set 或 List)的第 n 个元素可以通过使用方括号表示法获取,如下面的示例所示。
如果索引集合是 java.util.List,则可以通过 list.get(n) 直接访问第 n 个元素。 对于任何其他类型的 Collection,将通过使用其 Iterator 遍历集合并返回遇到的第 n 个元素来访问第 n 个元素。 |
-
Java
-
Kotlin
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
// Inventions Array
// evaluates to "Induction motor"
String invention = parser.parseExpression("inventions[3]").getValue(
context, tesla, String.class);
// Members List
// evaluates to "Nikola Tesla"
String name = parser.parseExpression("members[0].name").getValue(
context, ieee, String.class);
// List and Array Indexing
// evaluates to "Wireless communication"
String invention = parser.parseExpression("members[0].inventions[6]").getValue(
context, ieee, String.class);
val parser = SpelExpressionParser()
val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()
// Inventions Array
// evaluates to "Induction motor"
val invention = parser.parseExpression("inventions[3]").getValue(
context, tesla, String::class.java)
// Members List
// evaluates to "Nikola Tesla"
val name = parser.parseExpression("members[0].name").getValue(
context, ieee, String::class.java)
// List and Array Indexing
// evaluates to "Wireless communication"
val invention = parser.parseExpression("members[0].inventions[6]").getValue(
context, ieee, String::class.java)
Indexing into Strings
字符串的第 n 个字符可以通过在方括号中指定索引来获取,如下面的示例所示。
字符串的 nth 字符将计算为 |
-
Java
-
Kotlin
// evaluates to "T" (8th letter of "Nikola Tesla")
String character = parser.parseExpression("members[0].name[7]")
.getValue(societyContext, String.class);
// evaluates to "T" (8th letter of "Nikola Tesla")
val character = parser.parseExpression("members[0].name[7]")
.getValue(societyContext, String::class.java)
Indexing into Maps
映射的内容是通过在方括号中指定键值来获取的。 在以下示例中,由于 officers 映射的键是字符串,因此我们可以指定诸如 “president'” 的字符串字面量:
-
Java
-
Kotlin
// Officer's Map
// evaluates to Inventor("Pupin")
Inventor pupin = parser.parseExpression("officers['president']")
.getValue(societyContext, Inventor.class);
// evaluates to "Idvor"
String city = parser.parseExpression("officers['president'].placeOfBirth.city")
.getValue(societyContext, String.class);
String countryExpression = "officers['advisors'][0].placeOfBirth.country";
// setting values
parser.parseExpression(countryExpression)
.setValue(societyContext, "Croatia");
// evaluates to "Croatia"
String country = parser.parseExpression(countryExpression)
.getValue(societyContext, String.class);
// Officer's Map
// evaluates to Inventor("Pupin")
val pupin = parser.parseExpression("officers['president']")
.getValue(societyContext, Inventor::class.java)
// evaluates to "Idvor"
val city = parser.parseExpression("officers['president'].placeOfBirth.city")
.getValue(societyContext, String::class.java)
val countryExpression = "officers['advisors'][0].placeOfBirth.country"
// setting values
parser.parseExpression(countryExpression)
.setValue(societyContext, "Croatia")
// evaluates to "Croatia"
val country = parser.parseExpression(countryExpression)
.getValue(societyContext, String::class.java)
Indexing into Objects
可以通过在方括号中指定属性的名称来获取对象的属性。 这类似于基于其键访问映射的值。 以下示例演示了如何索引到对象以检索特定属性。
-
Java
-
Kotlin
// Create an inventor to use as the root context object.
Inventor tesla = new Inventor("Nikola Tesla");
// evaluates to "Nikola Tesla"
String name = parser.parseExpression("#root['name']")
.getValue(context, tesla, String.class);
// Create an inventor to use as the root context object.
val tesla = Inventor("Nikola Tesla")
// evaluates to "Nikola Tesla"
val name = parser.parseExpression("#root['name']")
.getValue(context, tesla, String::class.java)
Indexing into Custom Structures
从 Spring Framework 6.2 开始,Spring 表达式语言支持通过允许开发者实现并注册带有 IndexAccessor
的 EvaluationContext
的自定义结构进行索引。如果你想要支持依赖自定义索引访问器的 compilation 表达式,该索引访问器必须实现 CompilableIndexAccessor
SPI。
为了支持常见用例,Spring 提供了一个内置的 ReflectiveIndexAccessor,它是一个灵活的 IndexAccessor,它使用反射从目标对象的索引结构中读取并选择性地写入。 索引结构可以通过 public 读方法(在读取时)或 public 写方法(在写入时)进行访问。 读方法和写方法之间的关系基于一个惯例,该惯例适用于索引结构的典型实现。
|
以下代码清单定义了一个 Color 枚举和一个 FruitMap 类型,它的行为类似于映射但并不实现 java.util.Map 接口。 因此,如果要在 SpEL 表达式中的 FruitMap 中进行索引,则需要注册一个 IndexAccessor。
public enum Color {
RED, ORANGE, YELLOW
}
public class FruitMap {
private final Map<Color, String> map = new HashMap<>();
public FruitMap() {
this.map.put(Color.RED, "cherry");
this.map.put(Color.ORANGE, "orange");
this.map.put(Color.YELLOW, "banana");
}
public String getFruit(Color color) {
return this.map.get(color);
}
public void setFruit(Color color, String fruit) {
this.map.put(color, fruit);
}
}
可以 new
ReflectiveIndexAccessor(FruitMap.class, Color.class, "getFruit")
创建 FruitMap
的只读 IndexAccessor
。通过注册该访问器并注册 FruitMap
作为名为 #fruitMap
的变量,SpEL 表达式 #fruitMap[T(example.Color).RED]
将评估为 "cherry"
。
可以 new
ReflectiveIndexAccessor(FruitMap.class, Color.class, "getFruit", "setFruit")
创建 FruitMap
的可读写 IndexAccessor
。通过注册该访问器并注册 FruitMap
作为名为 #fruitMap
的变量,SpEL 表达式 #fruitMap[T(example.Color).RED] = 'strawberry'
可用于将红色颜色的水果映射从 "cherry"
更改为 "strawberry"
。
以下示例演示了如何注册一个 ReflectiveIndexAccessor
以在 FruitMap
中建立索引,然后在 SpEL 表达式中对 FruitMap
建立索引。
-
Java
-
Kotlin
// Create a ReflectiveIndexAccessor for FruitMap
IndexAccessor fruitMapAccessor = new ReflectiveIndexAccessor(
FruitMap.class, Color.class, "getFruit", "setFruit");
// Register the IndexAccessor for FruitMap
context.addIndexAccessor(fruitMapAccessor);
// Register the fruitMap variable
context.setVariable("fruitMap", new FruitMap());
// evaluates to "cherry"
String fruit = parser.parseExpression("#fruitMap[T(example.Color).RED]")
.getValue(context, String.class);
// Create a ReflectiveIndexAccessor for FruitMap
val fruitMapAccessor = ReflectiveIndexAccessor(
FruitMap::class.java, Color::class.java, "getFruit", "setFruit")
// Register the IndexAccessor for FruitMap
context.addIndexAccessor(fruitMapAccessor)
// Register the fruitMap variable
context.setVariable("fruitMap", FruitMap())
// evaluates to "cherry"
val fruit = parser.parseExpression("#fruitMap[T(example.Color).RED]")
.getValue(context, String::class.java)