12. 表达式语言(EL)
Web Flow 使用EL访问其数据模型并调用作。 本章应帮助你熟悉EL语法、配置以及你可以参考流程定义中的特殊EL变量。
EL在流程中用于多种用途,包括:
-
访问客户端数据,比如声明流输入或引用请求参数。
-
在 Web Flow 中访问数据
请求上下文如flowScope(流控镜)或当前事件. -
通过动作调用Spring管理对象的方法。
-
解析表达式,如状态转换标准、子流ID和视图名称。
EL也用于将表单参数绑定到模型对象,反之,也可以从模型对象的属性渲染格式化表单字段。 但使用 Web Flow 配合 JSF 时则不适用。 在这种情况下,标准的 JSF 组件生命周期适用。
12.1. 表达式类型
一个重要的概念是,Web Flow中有两种表达式:
12.1.1. 标准表达
第一种也是最常见的表达式是标准表达式。此类表达式由EL直接计算,无需用分隔符(如 )。以下示例展示了此类标准表达式:\#{}
<evaluate expression="searchCriteria.nextPage()" />
前面的表达式是一个标准表达式,调用下一页方法检索标准变量。如果你尝试用特殊的分隔符(例如),将该表达式包围起来,得到\#{}IllegalArgumentException. 在此语境下,分隔符被视为冗余的。唯一可接受的值表达属性是一个表达式字符串。
12.1.2. 模板表达式
第二种表达式是模板表达式。模板表达式允许将文字文本与一个或多个标准表达式混合使用。每个标准表达式块都被明确的分隔符包围。以下示例展示了一个模板表达式:\#{}
<view-state id="error" view="error-#{externalContext.locale}.xhtml" />
前面的表达式是一个模板表达式。评估结果是一个字符串,连接了以下文字文本错误-和.xhtml结果为externalContext.locale. 你需要明确的分隔符来划分模板中的标准表达块。
| 请参阅Web Flow XML schema,完整列出接受标准表达式和接受模板表达式的XML属性。你也可以在Eclipse中使用F2(或其他IDE中的等效快捷方式)在输入特定流定义属性时访问可用文档。 |
12.2. EL实现
Spring Web Flow 支持以下 EL(表达式语言)实现:
12.2.1. 春季EL
Web Flow 使用 Spring 表达式语言(Spring EL)。Spring EL 旨在为 Spring 产品组合中的所有产品提供一种单一且支持良好的表达式语言。它作为一个独立的 jar 分发(org.springframework.expression)在春季框架中。
12.2.2. 统一EL
统一EL的使用也意味着依赖于艾尔-阿皮,尽管这通常由你的网页容器提供。虽然 Spring EL 是默认且推荐使用的表达式语言,但你可以用 Unified EL 替代它。为此,你需要以下 Spring 配置来插入WebFlowELExpressionParser前往流量-构建器-服务:
<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配置为包含这些自定义转换器的转换服务,具体如下:
<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。
从统一EL升级到Spring EL时,请注意以下几个小变化:
-
用流中定义表示的表达式必须更改为(注意前置退格字符)。
${}\#{} -
测试当前事件的表达式(例如
#{currentEvent == 'submit'}必须将 改为\#{currentEvent.id == '提交'}(注意新增身份证). -
解析性质(例如:
#{currentUser.name}) 可能导致NullPointerException没有任何检查,如\#{currentUser != null ? currentUser.name : null}. 一个更好的选择是安全导航操作员:\#{currentUser?.姓名}.
有关 Spring EL 语法的更多信息,请参见 Spring 文档中的语言参考部分。
12.4. 特殊EL变量
你可以在流程中引用多个隐式变量。
请记住这个通用规则:
你应该使用指向数据作用域的变量(flowScope(流控镜),视图范围,请求范围,依此类推)只有在你为某个作用域赋予新变量时才会这样。
例如,当将调用结果分配给bookingService.findHotels(searchCriteria)到一个新的变量,称为酒店你必须在它前面加上一个范围变量,让 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>
然而,当设置已有变量(例如)检索标准在下面的例子中,你应该直接引用该变量,不要在前缀任何作用域变量,具体如下:
<?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(流控镜)来分配一个流变量。
流量示范域在流开始时分配,流结束时被销毁。
默认实现中,流域中存储的任何对象都需要可序列化。
以下列表定义了flowScope(流控镜)变量:
<evaluate expression="searchService.findHotel(hotelId)" result="flowScope.hotel" />
12.4.2. 该视图范围变量
你可以使用视图范围用于分配视图变量。
当视图状态进入并销毁,国家退出时。
视图范围只能在视图状态.
默认实现中,视图范围内存储的任何对象都需要可序列化。
以下列表定义了视图范围变量:
<on-render>
<evaluate expression="searchService.findHotels(searchCriteria)" result="viewScope.hotels"
result-type="dataModel" />
</on-render>
12.4.3. 该请求范围变量
你可以使用请求范围来分配请求变量。
请求范围在调用流时分配,流返回时被销毁。
以下列表定义了请求范围变量:
<set name="requestScope.hotelId" value="requestParameters.id" type="long" />
12.4.4. 该闪光镜变量
你可以使用闪光镜来分配一个闪烁变量。
Flash Scope在流程开始时被分配,每次视图渲染后清除,流程结束时销毁。
默认实现中,存储在闪存范围中的对象必须可序列化。
以下列表定义了闪光镜变量:
<set name="flashScope.statusMessage" value="'Booking confirmed'" />
12.4.5. 该对话范围变量
你可以使用对话范围来分配一个对话变量。
当顶层流程开始时,会话范围被分配,顶层流程结束时则被销毁。
对话范围由顶层流程及其所有子流程共享。
默认实现中,对话范围对象存储在 HTTP 会话中,通常应可序列化以满足典型会话复制的需求。
以下列表定义了对话范围变量:
<evaluate expression="searchService.findHotel(hotelId)" result="conversationScope.hotel" />
12.4.6. 该请求参数变量
这请求参数变量访问客户端请求参数,具体如下:
<set name="requestScope.hotelId" value="requestParameters.id" type="long" />
12.4.7. 该当前事件变量
这当前事件变量访问当前的属性事件如下:
<evaluate expression="booking.guests.add(currentEvent.attributes.guest)" />
12.4.8. 那currentUser变量
这currentUser变量访问已认证的主要如下:
<evaluate expression="bookingService.createBooking(hotelId, currentUser.name)"
result="flowScope.booking" />
12.4.9. 那消息上下文变量
这消息上下文变量访问上下文以检索并创建流执行消息,包括错误和成功消息。
参见消息上下文更多信息请参见Javadocs。
以下示例使用了消息上下文变量:
<evaluate expression="bookingValidator.validate(booking, messageContext)" />
12.4.10. 那个resourceBundle变量
这resourceBundle变量访问消息资源,具体如下:
<set name="flashScope.successMessage" value="resourceBundle.successMessage" />
12.4.11. 那flowRequestContext变量
这flowRequestContext变量访问请求上下文API,是当前流请求的表示。
更多信息请参见API Javadocs。
12.4.12. 那个flowExecutionContext变量
这flowExecutionContext变量访问FlowExecutionContextAPI,是当前流状态的表示。
更多信息请参见API Javadocs。
12.4.14.外部上下文变量
这外部上下文变量访问客户端环境,包括用户会话属性。
参见外部语境 更多信息请使用 API JavaDocs。
以下示例使用了外部上下文变量:
<evaluate expression="searchService.suggestHotels(externalContext.sessionMap.userProfile)"
result="viewScope.hotels" />
12.5. 范围搜索算法
如本节前文所述,在分配某个流程范围变量时,必须引用该范围。 以下示例展示了如何实现:
<set name="requestScope.hotelId" value="requestParameters.id" type="long" />
当你仅访问某个作用域中的变量时,引用该作用域是可选的,具体如下:
<evaluate expression="entityManager.persist(booking)" />
当没有指定范围时,比如使用预订如前所述,使用了范围搜索算法。
算法会在请求、闪现、视图、流和对话等范围内查找该变量。
如果找不到这样的变量,则评估例外被抛出。