12. 表达式语言 (EL)

Web Flow 使用 EL 访问其数据模型并调用作。 本章应让您熟悉 EL 语法、配置和您可以从流定义中引用的特殊 EL 变量。spring-doc.cadn.net.cn

EL 用于流程中的许多事情,包括:spring-doc.cadn.net.cn

EL 还用于将表单参数绑定到模型对象,反之,从模型对象的属性渲染格式化的表单字段。 但是,当将 Web Flow 与 JSF 一起使用时,这并不适用。 在这种情况下,标准 JSF 组件生命周期适用。spring-doc.cadn.net.cn

12.1. 表达式类型

需要理解的一个重要概念是 Web Flow 中有两种类型的表达式:spring-doc.cadn.net.cn

12.1.1. 标准表达式

第一种也是最常见的表达式类型是标准表达式。 此类表达式由 EL 直接计算,无需括在分隔符中,例如 . 以下示例显示了此类标准表达式:\#{}spring-doc.cadn.net.cn

<evaluate expression="searchCriteria.nextPage()" />

前面的表达式是一个标准表达式,它调用nextPage方法上的searchCriteriavariable 的值。 如果尝试将此表达式括在特殊分隔符(例如 )中,则会得到一个\#{}IllegalArgumentException. 在这种情况下,分隔符被视为冗余。 唯一可接受的expressionattribute 是单个表达式字符串。spring-doc.cadn.net.cn

12.1.2. 模板表达式

第二种类型的表达式是模板表达式。 模板表达式允许将文本文本与一个或多个标准表达式混合。 每个标准表达式块都显式地用分隔符括起来。 以下示例显示了一个模板表达式:\#{}spring-doc.cadn.net.cn

<view-state id="error" view="error-#{externalContext.locale}.xhtml" />

上述表达式是模板表达式。 评估结果是一个字符串,该字符串连接文本,例如error-.xhtml与评估结果externalContext.locale. 此处需要显式分隔符来划分模板中的标准表达式块。spring-doc.cadn.net.cn

有关接受标准表达式的 XML 属性和接受模板表达式的 XML 属性的完整列表,请参阅 Web 流 XML 架构。 您还可以在 Eclipse 中使用 F2(或其他 IDE 中的等效快捷方式)在键入特定流定义属性时访问可用文档。

12.2. EL 实现

Spring Web Flow 支持以下 EL(表达式语言)实现:spring-doc.cadn.net.cn

12.2.1. Spring EL

Web Flow 使用 Spring 表达式语言 (Spring EL)。Spring EL 的创建是为了提供一种单一的、得到充分支持的表达式语言,供 Spring 产品组合中的所有产品使用。 它作为单独的 jar (org.springframework.expression) 在 Spring 框架中。spring-doc.cadn.net.cn

12.2.2. 统一 EL

使用统一 EL 还意味着依赖于el-api,尽管这通常由 Web 容器提供。 尽管 Spring EL 是默认和推荐使用的表达式语言,但您可以将其替换为 Unified EL。 为此,您需要以下 Spring 配置来插入WebFlowELExpressionParserflow-builder-services:spring-doc.cadn.net.cn

<webflow:flow-builder-services expression-parser="expressionParser"/>

<bean id="expressionParser" class="org.springframework.webflow.expression.el.WebFlowELExpressionParser">
    <constructor-arg>
        <bean class="org.jboss.el.ExpressionFactoryImpl" />
    </constructor-arg>
</bean>

请注意,如果您的应用程序注册了自定义转换器,请务必确保WebFlowELExpressionParser配置了具有这些自定义转换器的 Conversion Service,如下所示:spring-doc.cadn.net.cn

<webflow:flow-builder-services expression-parser="expressionParser" conversion-service="conversionService"/>

<bean id="expressionParser" class="org.springframework.webflow.expression.el.WebFlowELExpressionParser">
    <constructor-arg>
        <bean class="org.jboss.el.ExpressionFactoryImpl" />
    </constructor-arg>
    <property name="conversionService" ref="conversionService"/>
</bean>

<bean id="conversionService" class="somepackage.ApplicationConversionService"/>

12.3. EL 可移植性

通常,您会发现 Spring EL 和 Unified EL 具有非常相似的语法。spring-doc.cadn.net.cn

但是,Spring El 有一些优势。 例如,Spring EL 与 Spring 3 的类型转换紧密集成,这使您可以充分利用其功能。 具体来说,目前只有 Spring EL 支持泛型类型的自动检测以及格式注释的使用。spring-doc.cadn.net.cn

从 Unified EL 升级到 Spring EL 时,请记住以下细微更改:spring-doc.cadn.net.cn

  • 流定义中描绘的表达式必须更改为(请注意前导退格字符)。${}\#{}spring-doc.cadn.net.cn

  • 测试当前事件的表达式(例如#{currentEvent == 'submit'}) 必须更改为\#{currentEvent.id == 'submit'}(请注意,添加了id).spring-doc.cadn.net.cn

  • 解析属性(例如#{currentUser.name}) 可能会导致NullPointerException没有任何检查,例如\#{currentUser != null ? currentUser.name : null}.一个更好的选择是 safe navigation 运算符:\#{currentUser?.name}.spring-doc.cadn.net.cn

有关 Spring EL 语法的更多信息,请参阅 Spring 文档中的 Language Reference 部分。spring-doc.cadn.net.cn

12.4. 特殊 EL 变量

您可以从流程中引用多个隐式变量。spring-doc.cadn.net.cn

请记住以下一般规则: 您应该使用引用数据范围 (flowScope,viewScope,requestScope等),仅当您将新变量分配给其中一个范围时。spring-doc.cadn.net.cn

例如,当将调用结果分配给bookingService.findHotels(searchCriteria)转换为名为hotels,则必须为其添加 scope 变量前缀,以便让 Web Flow 知道您希望将其存储在哪里。 以下示例显示了如何执行此作:spring-doc.cadn.net.cn

<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns="http://www.springframework.org/schema/webflow" ... >

	<var name="searchCriteria" class="org.springframework.webflow.samples.booking.SearchCriteria" />

	<view-state id="reviewHotels">
		<on-render>
			<evaluate expression="bookingService.findHotels(searchCriteria)" result="viewScope.hotels" />
		</on-render>
	</view-state>

</flow>

但是,当设置现有变量(例如searchCriteria在以下示例中),您应该直接引用变量,而不为其添加任何范围变量的前缀,如下所示:spring-doc.cadn.net.cn

<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns="http://www.springframework.org/schema/webflow" ... >

	<var name="searchCriteria" class="org.springframework.webflow.samples.booking.SearchCriteria" />

	<view-state id="reviewHotels">
		<transition on="sort">
			<set name="searchCriteria.sortBy" value="requestParameters.sortBy" />
		</transition>
	</view-state>

</flow>

以下是您可以在流程定义中引用的隐式变量列表:spring-doc.cadn.net.cn

12.4.1. 使用flowScope变量

您可以使用flowScope以分配流变量。 流范围在流开始时分配,在流结束时销毁。 使用默认实现,存储在 flow scope 中的任何对象都需要是可序列化的。 下面的清单定义了一个flowScope变量:spring-doc.cadn.net.cn

<evaluate expression="searchService.findHotel(hotelId)" result="flowScope.hotel" />

12.4.2. 使用viewScope变量

您可以使用viewScope以分配视图变量。 视图范围在view-state进入状态,并在状态退出时销毁。 View 范围只能view-state. 使用默认实现,存储在视图范围内的任何对象都需要是可序列化的。 下面的清单定义了一个viewScope变量:spring-doc.cadn.net.cn

<on-render>
    <evaluate expression="searchService.findHotels(searchCriteria)" result="viewScope.hotels"
              result-type="dataModel" />
</on-render>

12.4.3. 使用requestScope变量

您可以使用requestScope以分配请求变量。 请求范围在调用流时分配,并在流返回时销毁。 下面的清单定义了一个requestScope变量:spring-doc.cadn.net.cn

<set name="requestScope.hotelId" value="requestParameters.id" type="long" />

12.4.4. 使用flashScope变量

您可以使用flashScope以分配 Flash 变量。 Flash 范围在流启动时分配,在每次视图渲染后清除,并在流结束时销毁。 使用默认实现,存储在 flash scope 中的任何对象都需要是可序列化的。 下面的清单定义了一个flashScope变量:spring-doc.cadn.net.cn

<set name="flashScope.statusMessage" value="'Booking confirmed'" />

12.4.5. 使用conversationScope变量

您可以使用conversationScope以分配 conversation 变量。 对话范围在顶级流开始时分配,并在顶级流结束时销毁。 对话范围由顶级流及其所有子流共享。 使用默认实现,对话范围的对象存储在 HTTP 会话中,并且通常应该是可序列化的,以考虑典型的会话复制。 下面的清单定义了一个conversationScope变量:spring-doc.cadn.net.cn

<evaluate expression="searchService.findHotel(hotelId)" result="conversationScope.hotel" />

12.4.6. 使用requestParameters变量

requestParametersvariable 访问客户端请求参数,如下所示:spring-doc.cadn.net.cn

<set name="requestScope.hotelId" value="requestParameters.id" type="long" />

12.4.7. 使用currentEvent变量

currentEventvariable 访问当前Event如下:spring-doc.cadn.net.cn

<evaluate expression="booking.guests.add(currentEvent.attributes.guest)" />

12.4.8. 使用currentUser变量

currentUser变量访问经过身份验证的Principal如下:spring-doc.cadn.net.cn

<evaluate expression="bookingService.createBooking(hotelId, currentUser.name)"
          result="flowScope.booking" />

12.4.9. 使用messageContext变量

messageContext变量访问上下文以检索和创建流执行消息,包括错误和成功消息。 请参阅MessageContextJavadocs 了解更多信息。 以下示例使用messageContext变量:spring-doc.cadn.net.cn

<evaluate expression="bookingValidator.validate(booking, messageContext)" />

12.4.10. 使用resourceBundle变量

resourceBundle变量访问消息资源,如下所示:spring-doc.cadn.net.cn

<set name="flashScope.successMessage" value="resourceBundle.successMessage" />

12.4.11. 使用flowRequestContext变量

flowRequestContext变量访问RequestContextAPI,它是当前流请求的表示形式。 有关更多信息,请参阅 API Javadocsspring-doc.cadn.net.cn

12.4.12. 使用flowExecutionContext变量

flowExecutionContext变量访问FlowExecutionContextAPI,它是当前流状态的表示形式。 有关更多信息,请参阅 API Javadocsspring-doc.cadn.net.cn

12.4.13. 使用flowExecutionUrl变量

flowExecutionUrlvariable 访问当前流程执行视图状态的上下文相对 URI。spring-doc.cadn.net.cn

12.4.14. 使用externalContext变量

externalContext变量访问客户端环境,包括用户会话属性。 请参阅ExternalContext API JavaDocs 了解更多信息。 以下示例使用externalContext变量:spring-doc.cadn.net.cn

<evaluate expression="searchService.suggestHotels(externalContext.sessionMap.userProfile)"
          result="viewScope.hotels" />

12.5. 范围搜索算法

如本节前面所述,在其中一个流范围中分配变量时,需要引用该范围。 以下示例显示了如何执行此作:spring-doc.cadn.net.cn

<set name="requestScope.hotelId" value="requestParameters.id" type="long" />

当您仅访问其中一个 scope 中的变量时,引用 scope 是可选的,如下所示:spring-doc.cadn.net.cn

<evaluate expression="entityManager.persist(booking)" />

如果未指定范围,如使用booking前面所示,使用了范围搜索算法。 该算法在 request、flash、view、flow 和 conversation 范围内查找变量。 如果未找到此类变量,则EvaluationException被抛出。spring-doc.cadn.net.cn