此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Framework 6.2.10spring-doc.cadn.net.cn

安全导航操作员

安全导航操作员 (?.) 用于避免NullPointerException并来 来自 Groovy 语言。通常,当您有对对象的引用时,您可能需要验证 事实并非如此null在访问对象的方法或属性之前。避免 此,安全导航运算符返回null用于特定的空安全作 而不是抛出异常。spring-doc.cadn.net.cn

当安全导航操作员评估为null对于特定的空安全 在复合表达式中作,则复合表达式的其余部分将 仍在评估中。spring-doc.cadn.net.cn

安全属性和方法访问

以下示例演示如何使用安全导航运算符进行属性访问 (?.).spring-doc.cadn.net.cn

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();

Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
tesla.setPlaceOfBirth(new PlaceOfBirth("Smiljan"));

// evaluates to "Smiljan"
String city = parser.parseExpression("placeOfBirth?.city") (1)
		.getValue(context, tesla, String.class);

tesla.setPlaceOfBirth(null);

// evaluates to null - does not throw NullPointerException
city = parser.parseExpression("placeOfBirth?.city") (2)
		.getValue(context, tesla, String.class);
1 在非 null 上使用安全导航运算符placeOfBirth属性
2 在 null 上使用安全导航运算符placeOfBirth属性
val parser = SpelExpressionParser()
val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()

val tesla = Inventor("Nikola Tesla", "Serbian")
tesla.setPlaceOfBirth(PlaceOfBirth("Smiljan"))

// evaluates to "Smiljan"
var city = parser.parseExpression("placeOfBirth?.city") (1)
		.getValue(context, tesla, String::class.java)

tesla.setPlaceOfBirth(null)

// evaluates to null - does not throw NullPointerException
city = parser.parseExpression("placeOfBirth?.city") (2)
		.getValue(context, tesla, String::class.java)
1 在非 null 上使用安全导航运算符placeOfBirth属性
2 在 null 上使用安全导航运算符placeOfBirth属性

安全导航运算符还适用于对象上的方法调用。spring-doc.cadn.net.cn

例如,表达式#calculator?.max(4, 2)评估为null如果#calculator变量尚未在上下文中配置。否则,max(int, int)方法将在#calculator.spring-doc.cadn.net.cn

安全索引访问

从 Spring Framework 6.2 开始,Spring 表达式语言支持 索引到以下类型的结构中。spring-doc.cadn.net.cn

以下示例演示如何使用安全导航运算符将索引编入 列表 (?.[]).spring-doc.cadn.net.cn

ExpressionParser parser = new SpelExpressionParser();
IEEE society = new IEEE();
EvaluationContext context = new StandardEvaluationContext(society);

// evaluates to Inventor("Nikola Tesla")
Inventor inventor = parser.parseExpression("members?.[0]") (1)
		.getValue(context, Inventor.class);

society.members = null;

// evaluates to null - does not throw an exception
inventor = parser.parseExpression("members?.[0]") (2)
		.getValue(context, Inventor.class);
1 对非 null 使用 null 安全索引运算符members列表
2 对 null 使用 null 安全索引运算符members列表
val parser = SpelExpressionParser()
val society = IEEE()
val context = StandardEvaluationContext(society)

// evaluates to Inventor("Nikola Tesla")
var inventor = parser.parseExpression("members?.[0]") (1)
		.getValue(context, Inventor::class.java)

society.members = null

// evaluates to null - does not throw an exception
inventor = parser.parseExpression("members?.[0]") (2)
		.getValue(context, Inventor::class.java)
1 对非 null 使用 null 安全索引运算符members列表
2 对 null 使用 null 安全索引运算符members列表

安全馆藏选择和投影

Spring 表达式语言支持安全导航,通过 以下运算符。spring-doc.cadn.net.cn

以下示例演示如何使用安全导航运算符进行收集 选择 (?.?).spring-doc.cadn.net.cn

ExpressionParser parser = new SpelExpressionParser();
IEEE society = new IEEE();
StandardEvaluationContext context = new StandardEvaluationContext(society);
String expression = "members?.?[nationality == 'Serbian']"; (1)

// evaluates to [Inventor("Nikola Tesla")]
List<Inventor> list = (List<Inventor>) parser.parseExpression(expression)
		.getValue(context);

society.members = null;

// evaluates to null - does not throw a NullPointerException
list = (List<Inventor>) parser.parseExpression(expression)
		.getValue(context);
1 对潜在 null 使用 null 安全选择运算符members列表
val parser = SpelExpressionParser()
val society = IEEE()
val context = StandardEvaluationContext(society)
val expression = "members?.?[nationality == 'Serbian']" (1)

// evaluates to [Inventor("Nikola Tesla")]
var list = parser.parseExpression(expression)
		.getValue(context) as List<Inventor>

society.members = null

// evaluates to null - does not throw a NullPointerException
list = parser.parseExpression(expression)
		.getValue(context) as List<Inventor>
1 对潜在 null 使用 null 安全选择运算符members列表

以下示例演示如何使用“空安全优先选择”运算符 集合 (?.^).spring-doc.cadn.net.cn

ExpressionParser parser = new SpelExpressionParser();
IEEE society = new IEEE();
StandardEvaluationContext context = new StandardEvaluationContext(society);
String expression =
	"members?.^[nationality == 'Serbian' || nationality == 'Idvor']"; (1)

// evaluates to Inventor("Nikola Tesla")
Inventor inventor = parser.parseExpression(expression)
		.getValue(context, Inventor.class);

society.members = null;

// evaluates to null - does not throw a NullPointerException
inventor = parser.parseExpression(expression)
		.getValue(context, Inventor.class);
1 对潜在的 null 使用 “null-safe select first” 运算符members列表
val parser = SpelExpressionParser()
val society = IEEE()
val context = StandardEvaluationContext(society)
val expression =
	"members?.^[nationality == 'Serbian' || nationality == 'Idvor']" (1)

// evaluates to Inventor("Nikola Tesla")
var inventor = parser.parseExpression(expression)
		.getValue(context, Inventor::class.java)

society.members = null

// evaluates to null - does not throw a NullPointerException
inventor = parser.parseExpression(expression)
		.getValue(context, Inventor::class.java)
1 对潜在的 null 使用 “null-safe select first” 运算符members列表

以下示例演示如何使用“空安全选择最后”运算符 集合 (?.$).spring-doc.cadn.net.cn

ExpressionParser parser = new SpelExpressionParser();
IEEE society = new IEEE();
StandardEvaluationContext context = new StandardEvaluationContext(society);
String expression =
	"members?.$[nationality == 'Serbian' || nationality == 'Idvor']"; (1)

// evaluates to Inventor("Pupin")
Inventor inventor = parser.parseExpression(expression)
		.getValue(context, Inventor.class);

society.members = null;

// evaluates to null - does not throw a NullPointerException
inventor = parser.parseExpression(expression)
		.getValue(context, Inventor.class);
1 对潜在 null 使用 “null-safe select last” 运算符members列表
val parser = SpelExpressionParser()
val society = IEEE()
val context = StandardEvaluationContext(society)
val expression =
	"members?.$[nationality == 'Serbian' || nationality == 'Idvor']" (1)

// evaluates to Inventor("Pupin")
var inventor = parser.parseExpression(expression)
		.getValue(context, Inventor::class.java)

society.members = null

// evaluates to null - does not throw a NullPointerException
inventor = parser.parseExpression(expression)
		.getValue(context, Inventor::class.java)
1 对潜在 null 使用 “null-safe select last” 运算符members列表

以下示例演示如何使用安全导航运算符进行收集 投影 (?.!).spring-doc.cadn.net.cn

ExpressionParser parser = new SpelExpressionParser();
IEEE society = new IEEE();
StandardEvaluationContext context = new StandardEvaluationContext(society);

// evaluates to ["Smiljan", "Idvor"]
List placesOfBirth = parser.parseExpression("members?.![placeOfBirth.city]") (1)
		.getValue(context, List.class);

society.members = null;

// evaluates to null - does not throw a NullPointerException
placesOfBirth = parser.parseExpression("members?.![placeOfBirth.city]") (2)
		.getValue(context, List.class);
1 对非空使用空安全投影运算符members列表
2 对 null 使用 null 安全投影运算符members列表
val parser = SpelExpressionParser()
val society = IEEE()
val context = StandardEvaluationContext(society)

// evaluates to ["Smiljan", "Idvor"]
var placesOfBirth = parser.parseExpression("members?.![placeOfBirth.city]") (1)
		.getValue(context, List::class.java)

society.members = null

// evaluates to null - does not throw a NullPointerException
placesOfBirth = parser.parseExpression("members?.![placeOfBirth.city]") (2)
		.getValue(context, List::class.java)
1 对非空使用空安全投影运算符members列表
2 对 null 使用 null 安全投影运算符members列表

空安全作Optional

从 Spring Framework 7.0 开始,在java.util.Optional具有透明的展开语义。spring-doc.cadn.net.cn

具体来说,当空安全运算符应用于空运算符时 Optional,它将是 视为Optionalnull,后续作将评估为null. 但是,如果将空安全运算符应用于非空运算符Optional这 后续作将应用于Optional从而 有效地解开Optional.spring-doc.cadn.net.cn

例如,如果user是类型Optional<User>,表达式user?.name将 评估为null如果usernull空的 Optional否则 评估为nameuser有效user.get().getName()user.get().name分别用于属性或字段访问。spring-doc.cadn.net.cn

调用OptionalAPI 仍然支持空的 Optional.例如,如果name是类型Optional<String>,表达式name?.orElse('Unknown')将评估为"Unknown"如果name是一个空的Optional否则将评估为String包含在Optional如果name是一个 非空Optional有效name.get().spring-doc.cadn.net.cn

同样,如果names是类型Optional<List<String>>,表达式names?.?⁠[#this.length > 5]将评估为null如果namesnull空的 Optional否则将计算为包含长度 大于 5,有效names.get().stream().filter(s → s.length() > 5).toList().spring-doc.cadn.net.cn

相同的语义适用于前面提到的所有空安全运算符 章。spring-doc.cadn.net.cn

有关更多详细信息和示例,请参阅 javadoc 以获取以下运算符。spring-doc.cadn.net.cn

复合表达式中的空安全运算

如本节开头所述,当安全导航操作员 评估为null对于复合表达式中的特定空安全作, 仍将评估化合物表达式的其余部分。这意味着 安全导航运算符必须应用于整个复合表达式,以便 避免任何不需要的NullPointerException.spring-doc.cadn.net.cn

给定表达式#person?.address.city如果#personnull安全导航 运算符 (?.) 确保在尝试访问address属性#person.但是,由于#person?.address评估为null一个NullPointerException在尝试访问city属性null.为了解决这个问题,您可以在整个化合物中应用空安全导航 表达式,如#person?.address?.city.该表达式将安全地计算为null如果#person#person?.address评估为null.spring-doc.cadn.net.cn

以下示例演示如何使用“空安全优先选择”运算符 (?.^) 与空安全属性访问 (?.) 在化合物中 表达。如果membersnull,“空安全先选择”运算符的结果 (members?.^[nationality == 'Serbian']) 的计算结果为null,以及 安全导航操作员 (?.name)确保整个化合物表达 评估为null而不是抛出异常。spring-doc.cadn.net.cn

ExpressionParser parser = new SpelExpressionParser();
IEEE society = new IEEE();
StandardEvaluationContext context = new StandardEvaluationContext(society);
String expression = "members?.^[nationality == 'Serbian']?.name"; (1)

// evaluates to "Nikola Tesla"
String name = parser.parseExpression(expression)
		.getValue(context, String.class);

society.members = null;

// evaluates to null - does not throw a NullPointerException
name = parser.parseExpression(expression)
		.getValue(context, String.class);
1 在复合表达式中使用“空安全优先选择”和空安全属性访问运算符。
val parser = SpelExpressionParser()
val society = IEEE()
val context = StandardEvaluationContext(society)
val expression = "members?.^[nationality == 'Serbian']?.name" (1)

// evaluates to "Nikola Tesla"
String name = parser.parseExpression(expression)
		.getValue(context, String::class.java)

society.members = null

// evaluates to null - does not throw a NullPointerException
name = parser.parseExpression(expression)
		.getValue(context, String::class.java)
1 在复合表达式中使用“空安全优先选择”和空安全属性访问运算符。