对于最新的稳定版本,请使用 Spring Framework 7.0.6!spring-doc.cadn.net.cn

属性、数组、列表、映射和索引器

Spring表达式语言为遍历对象图和索引各种结构提供了支持。spring-doc.cadn.net.cn

数值索引从零开始,例如在Java中访问数组的第nth个元素时。
查看安全导航运算符部分以了解如何使用空指针安全运算符遍历对象图并索引各种结构的详细信息。

属性导航

您可以通过在对象图中使用句点(.)来指示嵌套属性值,以此导航属性引用。Inventor类的实例,pupintesla,已使用示例中使用的类部分列出的数据进行填充。为了向下导航对象图并获取Tesla的出生年份以及Pupin的出生城市,我们使用以下表达式:spring-doc.cadn.net.cn

// 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

属性名称的第一个字母不区分大小写。因此,上面示例中的表达式可以分别写成Birthdate.Year + 1900PlaceOfBirth.City。此外,还可以通过方法调用可选地访问属性 — 例如,使用getPlaceOfBirth().getCity()代替placeOfBirth.cityspring-doc.cadn.net.cn

数组和集合的索引

数组或集合(例如,SetList)的第nth个元素可以通过使用方括号表示法获得,如下例所示。spring-doc.cadn.net.cn

如果索引集合是java.util.List,那么nth元素将直接通过list.get(n)访问。spring-doc.cadn.net.cn

对于Collection以外的任何类型,nth元素将通过使用其Iterator迭代集合并返回遇到的nth元素来访问。spring-doc.cadn.net.cn

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)

在字符串中索引

一个字符串中的第nth个字符可以通过在方括号中指定索引来获取,如下例所示。spring-doc.cadn.net.cn

字符串的第nth个字符将评估为java.lang.String,而不是java.lang.Character
// 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)

映射中的索引

地图的内容是通过在方括号中指定键值来获取的。在以下示例中,由于officers地图的键为字符串,因此我们可以指定诸如'president'这样的字符串字面量:spring-doc.cadn.net.cn

// 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)

对象索引

对象的属性可以通过在方括号中指定属性名称来获取。这类似于根据键访问映射中的值。以下示例演示了如何对对象进行索引以检索特定属性。spring-doc.cadn.net.cn

// 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)

自定义结构的索引

自Spring Framework 6.2起,Spring表达式语言通过允许开发者实现并注册一个与EvaluationContextIndexAccessor,支持对自定义结构的索引。如果您希望支持依赖于自定义索引访问器的表达式的编译,那么该索引访问器必须实现CompilableIndexAccessor SPI。spring-doc.cadn.net.cn

为支持常见用例,Spring提供了一个内置的ReflectiveIndexAccessor,这是一个灵活的IndexAccessor,使用反射读取并可选地写入目标对象的索引结构。索引结构可以通过一个 public 读方法(在读取时)或public 写方法(在写入时)访问。读方法和写方法之间的关系基于一种约定,适用于典型的索引结构实现。spring-doc.cadn.net.cn

ReflectiveIndexAccessor 同时也实现了 CompilableIndexAccessor 以便 支持 编译 为字节码以进行读取访问。但请注意,配置的读取方法必须能通过 public 类或 public 接口调用,编译才能成功。

以下代码清单定义了一个 Color 枚举和一个表现得像映射但未实现 java.util.Map 接口的 FruitMap 类型。因此,如果你想在 SpEL 表达式中对 FruitMap 进行索引,你需要注册一个 IndexAccessorspring-doc.cadn.net.cn

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);
	}
}

只读的 IndexAccessorFruitMap 可通过 new ReflectiveIndexAccessor(FruitMap.class, Color.class, "getFruit") 创建。注册了该访问器并以 FruitMap 注册为名为 #fruitMap 的变量后,SpEL 表达式 #fruitMap[T(example.Color).RED] 将计算为 "cherry"spring-doc.cadn.net.cn

读写型的 IndexAccessorFruitMap 可通过 new ReflectiveIndexAccessor(FruitMap.class, Color.class, "getFruit", "setFruit") 创建。注册此访问器后,将 FruitMap 作为名为 #fruitMap 的变量注册,SpEL 表达式 #fruitMap[T(example.Color).RED] = 'strawberry' 可用于将颜色红色的水果映射从 "cherry" 改为 "strawberry"spring-doc.cadn.net.cn

以下示例演示了如何在SpEL表达式中注册一个ReflectiveIndexAccessor用于索引到FruitMap,然后进一步索引到FruitMap中。spring-doc.cadn.net.cn

// 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)