屬性、陣列、列表、對映和索引器

Spring Expression Language 支援導航物件圖並對各種結構進行索引。

數值索引值是基於零的,例如在 Java 中訪問陣列的第 n 個元素時。
有關如何使用 null 安全運算子導航物件圖並對各種結構進行索引的詳細資訊,請參閱安全導航運算子一節。

屬性導航

您可以使用句點(.)來指示巢狀的屬性值,從而在物件圖內導航屬性引用。Inventor 類的例項,pupintesla,已使用示例中使用的類部分列出的資料進行填充。要導航物件圖的下層並獲取 Tesla 的出生年份和 Pupin 的出生城市,我們使用以下表達式

  • 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

屬性名稱的第一個字母允許不區分大小寫。因此,上面示例中的表示式可以分別寫成 Birthdate.Year + 1900PlaceOfBirth.City。此外,屬性也可以選擇透過方法呼叫來訪問 — 例如,getPlaceOfBirth().getCity() 而不是 placeOfBirth.city

對陣列和集合進行索引

可以使用方括號表示法獲取陣列或集合(例如,SetList)的第 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)

對字串進行索引

可以透過在方括號內指定索引來獲取字串的第 n 個字元,如下例所示。

字串的第 n 個字元將求值為 java.lang.String,而不是 java.lang.Character
  • 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)

對對映進行索引

透過在方括號內指定鍵值來獲取對映的內容。在以下示例中,由於 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)

對物件進行索引

可以透過在方括號內指定屬性的名稱來獲取物件的屬性。這類似於根據鍵訪問對映的值。以下示例演示瞭如何對物件進行索引以檢索特定屬性。

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

對自定義結構進行索引

自 Spring Framework 6.2 起,Spring Expression Language 透過允許開發者實現 IndexAccessor 並將其註冊到 EvaluationContext 中來支援對自定義結構進行索引。如果您希望支援依賴自定義索引訪問器的表示式編譯,則該索引訪問器必須實現 CompilableIndexAccessor SPI。

為了支援常見的用例,Spring 提供了一個內建的 ReflectiveIndexAccessor,它是一個靈活的 IndexAccessor,使用反射從目標物件的索引結構中讀取(並可選擇寫入)。可以透過一個 public 讀取方法(讀取時)或一個 public 寫入方法(寫入時)訪問索引結構。讀取方法和寫入方法之間的關係基於適用於典型的索引結構實現的約定。

ReflectiveIndexAccessor 也實現了 CompilableIndexAccessor,以支援讀取訪問的位元組碼編譯。但請注意,配置的讀取方法必須可以透過 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)