12. 表达式语言 (EL)
Web Flow 使用 EL 访问其数据模型并调用作。 本章应让您熟悉 EL 语法、配置和您可以从流定义中引用的特殊 EL 变量。
EL 用于流程中的许多事情,包括:
-
访问客户端数据,例如声明流输入或引用请求参数。
-
访问 Web Flow 的
RequestContext
如flowScope
或currentEvent
. -
通过作调用 Spring 管理的对象上的方法。
-
解析表达式,例如状态转换条件、子流 ID 和视图名称。
EL 还用于将表单参数绑定到模型对象,反之,从模型对象的属性渲染格式化的表单字段。 但是,当将 Web Flow 与 JSF 一起使用时,这并不适用。 在这种情况下,标准 JSF 组件生命周期适用。
12.1. 表达式类型
需要理解的一个重要概念是 Web Flow 中有两种类型的表达式:
12.1.1. 标准表达式
第一种也是最常见的表达式类型是标准表达式。
此类表达式由 EL 直接计算,无需括在分隔符中,例如 .
以下示例显示了此类标准表达式:\#{}
<evaluate expression="searchCriteria.nextPage()" />
前面的表达式是一个标准表达式,它调用nextPage
方法上的searchCriteria
variable 的值。
如果尝试将此表达式括在特殊分隔符(例如 )中,则会得到一个\#{}
IllegalArgumentException
.
在这种情况下,分隔符被视为冗余。
唯一可接受的expression
attribute 是单个表达式字符串。
12.1.2. 模板表达式
第二种类型的表达式是模板表达式。
模板表达式允许将文本文本与一个或多个标准表达式混合。
每个标准表达式块都显式地用分隔符括起来。
以下示例显示了一个模板表达式:\#{}
<view-state id="error" view="error-#{externalContext.locale}.xhtml" />
上述表达式是模板表达式。
评估结果是一个字符串,该字符串连接文本,例如error-
和.xhtml
与评估结果externalContext.locale
.
此处需要显式分隔符来划分模板中的标准表达式块。
有关接受标准表达式的 XML 属性和接受模板表达式的 XML 属性的完整列表,请参阅 Web 流 XML 架构。 您还可以在 Eclipse 中使用 F2(或其他 IDE 中的等效快捷方式)在键入特定流定义属性时访问可用文档。 |
12.2. EL 实现
Spring Web Flow 支持以下 EL(表达式语言)实现:
12.2.1. Spring EL
Web Flow 使用 Spring 表达式语言 (Spring EL)。Spring EL 的创建是为了提供一种单一的、得到充分支持的表达式语言,供 Spring 产品组合中的所有产品使用。
它作为单独的 jar (org.springframework.expression
) 在 Spring 框架中。
12.2.2. 统一 EL
使用统一 EL 还意味着依赖于el-api
,尽管这通常由 Web 容器提供。
尽管 Spring EL 是默认和推荐使用的表达式语言,但您可以将其替换为 Unified EL。
为此,您需要以下 Spring 配置来插入WebFlowELExpressionParser
到flow-builder-services
:
<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,如下所示:
<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 El 有一些优势。 例如,Spring EL 与 Spring 3 的类型转换紧密集成,这使您可以充分利用其功能。 具体来说,目前只有 Spring EL 支持泛型类型的自动检测以及格式注释的使用。
从 Unified EL 升级到 Spring EL 时,请记住以下细微更改:
-
流定义中描绘的表达式必须更改为(请注意前导退格字符)。
${}
\#{}
-
测试当前事件的表达式(例如
#{currentEvent == 'submit'}
) 必须更改为\#{currentEvent.id == 'submit'}
(请注意,添加了id
). -
解析属性(例如
#{currentUser.name}
) 可能会导致NullPointerException
没有任何检查,例如\#{currentUser != null ? currentUser.name : null}
.一个更好的选择是 safe navigation 运算符:\#{currentUser?.name}
.
有关 Spring EL 语法的更多信息,请参阅 Spring 文档中的 Language Reference 部分。
12.4. 特殊 EL 变量
您可以从流程中引用多个隐式变量。
请记住以下一般规则:
您应该使用引用数据范围 (flowScope
,viewScope
,requestScope
等),仅当您将新变量分配给其中一个范围时。
例如,当将调用结果分配给bookingService.findHotels(searchCriteria)
转换为名为hotels
,则必须为其添加 scope 变量前缀,以便让 Web Flow 知道您希望将其存储在哪里。
以下示例显示了如何执行此作:
<?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
在以下示例中),您应该直接引用变量,而不为其添加任何范围变量的前缀,如下所示:
<?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>
以下是您可以在流程定义中引用的隐式变量列表:
12.4.1. 使用flowScope
变量
您可以使用flowScope
以分配流变量。
流范围在流开始时分配,在流结束时销毁。
使用默认实现,存储在 flow scope 中的任何对象都需要是可序列化的。
下面的清单定义了一个flowScope
变量:
<evaluate expression="searchService.findHotel(hotelId)" result="flowScope.hotel" />
12.4.2. 使用viewScope
变量
您可以使用viewScope
以分配视图变量。
视图范围在view-state
进入状态,并在状态退出时销毁。
View 范围只能从view-state
.
使用默认实现,存储在视图范围内的任何对象都需要是可序列化的。
下面的清单定义了一个viewScope
变量:
<on-render>
<evaluate expression="searchService.findHotels(searchCriteria)" result="viewScope.hotels"
result-type="dataModel" />
</on-render>
12.4.3. 使用requestScope
变量
您可以使用requestScope
以分配请求变量。
请求范围在调用流时分配,并在流返回时销毁。
下面的清单定义了一个requestScope
变量:
<set name="requestScope.hotelId" value="requestParameters.id" type="long" />
12.4.4. 使用flashScope
变量
您可以使用flashScope
以分配 Flash 变量。
Flash 范围在流启动时分配,在每次视图渲染后清除,并在流结束时销毁。
使用默认实现,存储在 flash scope 中的任何对象都需要是可序列化的。
下面的清单定义了一个flashScope
变量:
<set name="flashScope.statusMessage" value="'Booking confirmed'" />
12.4.5. 使用conversationScope
变量
您可以使用conversationScope
以分配 conversation 变量。
对话范围在顶级流开始时分配,并在顶级流结束时销毁。
对话范围由顶级流及其所有子流共享。
使用默认实现,对话范围的对象存储在 HTTP 会话中,并且通常应该是可序列化的,以考虑典型的会话复制。
下面的清单定义了一个conversationScope
变量:
<evaluate expression="searchService.findHotel(hotelId)" result="conversationScope.hotel" />
12.4.6. 使用requestParameters
变量
这requestParameters
variable 访问客户端请求参数,如下所示:
<set name="requestScope.hotelId" value="requestParameters.id" type="long" />
12.4.7. 使用currentEvent
变量
这currentEvent
variable 访问当前Event
如下:
<evaluate expression="booking.guests.add(currentEvent.attributes.guest)" />
12.4.8. 使用currentUser
变量
这currentUser
变量访问经过身份验证的Principal
如下:
<evaluate expression="bookingService.createBooking(hotelId, currentUser.name)"
result="flowScope.booking" />
12.4.9. 使用messageContext
变量
这messageContext
变量访问上下文以检索和创建流执行消息,包括错误和成功消息。
请参阅MessageContext
Javadocs 了解更多信息。
以下示例使用messageContext
变量:
<evaluate expression="bookingValidator.validate(booking, messageContext)" />
12.4.10. 使用resourceBundle
变量
这resourceBundle
变量访问消息资源,如下所示:
<set name="flashScope.successMessage" value="resourceBundle.successMessage" />
12.4.11. 使用flowRequestContext
变量
这flowRequestContext
变量访问RequestContext
API,它是当前流请求的表示形式。
有关更多信息,请参阅 API Javadocs。
12.4.12. 使用flowExecutionContext
变量
这flowExecutionContext
变量访问FlowExecutionContext
API,它是当前流状态的表示形式。
有关更多信息,请参阅 API Javadocs。
12.4.14. 使用externalContext
变量
这externalContext
变量访问客户端环境,包括用户会话属性。
请参阅ExternalContext
API JavaDocs 了解更多信息。
以下示例使用externalContext
变量:
<evaluate expression="searchService.suggestHotels(externalContext.sessionMap.userProfile)"
result="viewScope.hotels" />
12.5. 范围搜索算法
如本节前面所述,在其中一个流范围中分配变量时,需要引用该范围。 以下示例显示了如何执行此作:
<set name="requestScope.hotelId" value="requestParameters.id" type="long" />
当您仅访问其中一个 scope 中的变量时,引用 scope 是可选的,如下所示:
<evaluate expression="entityManager.persist(booking)" />
如果未指定范围,如使用booking
前面所示,使用了范围搜索算法。
该算法在 request、flash、view、flow 和 conversation 范围内查找变量。
如果未找到此类变量,则EvaluationException
被抛出。