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方法searchCriteria可变。 如果您尝试将此表达式括在特殊的分隔符(例如 )中,则会得到一个\#{}IllegalArgumentException. 在这种情况下,分隔符被视为多余的。 唯一可接受的值expression属性是单个表达式字符串。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 属性的完整列表,请参阅 Web 流 XML 架构。 在键入特定的流定义属性时,您还可以在 Eclipse 中使用 F2(或其他 IDE 中的等效快捷方式)来访问可用的文档。

12.2. EL 实现

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

12.2.1. 弹簧EL

Web Flow 使用 Spring 表达式语言 (Spring EL)。创建 Spring EL 是为了提供一种单一的、支持良好的表达式语言,以便在 Spring 产品组合中的所有产品中使用。 它作为一个单独的罐子分发(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配置了具有这些自定义转换器的转换服务,如下所示: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}.一个更好的选择是安全导航运营商:\#{currentUser?.name}.spring-doc.cadn.net.cn

有关 Spring EL 语法的更多信息,请参阅 Spring 文档中的语言参考部分。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,则必须在前缀为作用域变量,以便 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以分配流变量。 流范围在流启动时分配,并在流结束时销毁。 使用默认实现,存储在流作用域中的任何对象都需要是可序列化的。 以下列表定义了flowScope变量:spring-doc.cadn.net.cn

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

12.4.2.viewScope变量

您可以使用viewScope以分配视图变量。 当view-state在状态退出时进入并销毁。 视图范围只能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 作用域在流启动时分配,在每次视图渲染后清除,并在流结束时销毁。 使用默认实现,存储在闪存作用域中的任何对象都需要可序列化。 以下列表定义了flashScope变量:spring-doc.cadn.net.cn

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

12.4.5.conversationScope变量

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

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

12.4.6.requestParameters变量

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

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

12.4.7.currentEvent变量

currentEvent变量访问当前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变量

flowExecutionUrl变量访问当前流执行视图状态的上下文相对 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" />

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

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

当未指定范围时,如使用booking如前所述,使用了范围搜索算法。 该算法在请求、闪存、视图、流和对话作用域中查找变量。 如果未找到此类变量,则EvaluationException被抛出。spring-doc.cadn.net.cn