# 4. 表达式语言 ## 4.1.导言 Web Flow 使用 EL 访问其数据模型并调用操作。本章将让你熟悉 EL 语法、配置和你可以从流定义中引用的特殊 EL 变量。 EL 用于流中的许多事情,包括: 1. 访问客户端数据,例如声明流输入或引用请求参数。 2. 访问 Web 流中的`RequestContext`中的数据,例如`flowScope`或`currentEvent`。 3. 通过操作在 Spring 管理的对象上调用方法。 4. 解析表达式,如状态转换条件、子流 ID 和视图名称。 EL 还用于将表单参数绑定到模型对象,并从模型对象的属性反向呈现格式化的表单字段。然而,当使用 Web 流与 JSF 一起使用时,这种方法并不适用,在这种情况下,标准的 JSF 组件 Lifecyle 就适用了。 ### 4.1.1.表达式类型 要理解的一个重要概念是,在 Web 流中有两种类型的表达式:标准表达式和模板表达式。 #### 标准表达式 第一种也是最常见的一种表达式是*标准表达式*。这样的表达式由 EL 直接求值,不需要用`#{}`这样的分隔符括起来。例如: ``` ``` 上面的表达式是一个标准表达式,在计算`searchCriteria`变量时调用`nextPage`方法。如果你试图将这个表达式包含在一个特殊的分隔符中,比如`#{}`,你将得到一个`IllegalArgumentException`。在这种情况下,分隔符被视为多余的。`expression`属性唯一可接受的值是一个表达式字符串。 #### 模板表达式 第二种表达式是*模板表达式*。模板表达式允许将文本与一个或多个标准表达式混合在一起。每个标准表达式块都显式地被`#{}`分隔符包围。例如: ``` ``` 上面的表达式是一个模板表达式。求值的结果将是一个字符串,该字符串将诸如`error-`和`.xhtml`等文字文本与求值`externalContext.locale`的结果连接在一起。如你所见,这里需要显式分隔符来划分模板中的标准表达式块。 |[[note](images/note.png)|Note| |:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:---| |有关接受标准表达式和接受模板表达式的 XML 属性的完整列表,请参见 Web Flow XML 模式。
你还可以在 Eclipse 中使用 F2(或在其他 IDE 中使用等效的快捷方式)在键入特定的流定义属性时访问可用的文档。| | ## 4.2.EL 实现 ### 4.2.1. Spring El Web 流使用[Spring Expression Language](https://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html)( Spring el)。 Spring 创建 EL 是为了提供一种单一的、支持良好的表达式语言,用于在 Spring 产品组合中的所有产品中使用。在 Spring 框架中,它作为一个单独的 JAR`org.springframework.expression`分发。 ### 4.2.2.统一 El 使用[Unified EL](https://en.wikipedia.org/wiki/Unified_Expression_Language)还意味着对`el-api`的依赖,尽管你的 Web 容器通常是*提供*。 Spring 虽然 EL 是默认的和推荐使用的表达式语言,但如果你希望这样做,可以用统一的 EL 替换它。你需要以下 Spring 配置来将`WebFlowELExpressionParser`插入`flow-builder-services`: ``` ``` 请注意,如果你的应用程序正在注册自定义转换器,那么确保 WebFloweLexPressionParser 配置了具有这些自定义转换器的转换服务是很重要的。 ``` ``` ## 4.3.EL 便携性 通常,你会发现 Spring EL 和 Unified EL 具有非常相似的语法。 然而,请注意,EL 也有一些优点。例如 Spring EL 与 Spring 3 的类型转换紧密集成,这允许你充分利用其功能。特别是,当前仅在 Spring EL 中支持对泛型类型的自动检测以及对格式注释的使用。 在从 Unified EL 升级到 Spring EL 时,需要记住一些小的更改,如下所示: 1. 在流定义中用`${}`去毛的表达式必须更改为`#{}`。 2. 测试当前事件`#{currentEvent == 'submit'}`的表达式必须更改为`#{currentEvent.id == 'submit'}`。 3. 解析诸如`#{currentUser.name}`之类的属性可能会导致 nullpointerexception,而无需进行诸如`#{currentUser != null ? currentUser.name : null}`之类的检查。一个更好的选择是安全导航操作符`#{currentUser?.name}`。 有关 Spring EL 语法的更多信息,请参阅 Spring 文档中的[语言参考](http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/expressions.html#expressions-language-ref)部分。 ## 4.4.特殊 EL 变量 你可以从流中引用几个隐式变量。这些变量将在本节中讨论。 记住这条通则。引用数据作用域(FlowScope、ViewScope、RequestScope 等)的变量只应在为其中一个作用域分配新变量时使用。 例如,当将调用`bookingService.findHotels(searchCriteria)`的结果分配给一个名为“Hotels”的新变量时,你必须在它的前缀加上一个范围变量,以便让 Web 流知道你希望将它存储在哪里: ``` ``` 但是,当在下面的示例中设置诸如“SearchCriteria”之类的现有变量时,你可以直接引用该变量,而无需用任何作用域变量对其进行前缀: ``` ``` 以下是可以在流定义中引用的隐式变量列表: ### 4.4.1.flowscope 使用`flowScope`分配流变量。流作用域在流开始时被分配,在流结束时被销毁。对于默认的实现,存储在流作用域中的任何对象都需要是可序列化的。 ``` ``` ### 4.4.2.viewscope 使用`viewScope`分配一个视图变量。当`view-state`进入时,视图作用域被分配,而当状态退出时,视图作用域被销毁。视图作用域是*只有*可从`view-state`中引用的。对于默认实现,存储在视图作用域中的任何对象都需要是可序列化的。 ``` ``` ### 4.4.3.RequestScope 使用`requestScope`分配一个请求变量。当一个流被调用时,请求作用域被分配,当流返回时,请求作用域被销毁。 ``` ``` ### 4.4.4.FlashScope 使用`flashScope`分配一个 flash 变量。当一个流开始时,flash 作用域被分配,在每个视图呈现后被清除,当该流结束时被销毁。对于默认的实现,存储在 Flash 作用域中的任何对象都需要是可序列化的。 ``` ``` ### 4.4.5.ConversationScope 使用`conversationScope`分配一个会话变量。对话范围在顶级流启动时被分配,在顶级流结束时被销毁。会话范围由顶级流及其所有子流共享。对于默认的实现,对话范围的对象存储在 HTTP会话中,并且通常应该是可序列化的,以考虑典型的会话复制。 ``` ``` ### 4.4.6.requestParameters 使用`requestParameters`访问客户端请求参数: ``` ``` ### 4.4.7.currentEvent 使用`currentEvent`访问当前`Event`的属性: ``` ``` ### 4.4.8.currentuser 使用`currentUser`访问经过身份验证的`Principal`: ``` ``` ### 4.4.9.messagecontext 使用`messageContext`访问上下文以检索和创建流执行消息,包括错误和成功消息。有关更多信息,请参见`MessageContext`Javadocs。 ``` ``` ### 4.4.10.ResourceBundle 使用`resourceBundle`访问消息资源。 ``` ``` ### 4.4.11.FlowRequestContext 使用`flowRequestContext`访问`RequestContext`API,这是当前流请求的表示。有关更多信息,请参见 API Javadocs。 ### 4.4.12.FlowExecutionContext 使用`flowExecutionContext`访问`FlowExecutionContext`API,这是当前流状态的表示。有关更多信息,请参见 API Javadocs。 ### 4.4.13.flowexecutionurl 使用`flowExecutionUrl`访问当前流执行视图状态的上下文相关 URI。 ### 4.4.14.ExternalContext 使用`externalContext`访问客户端环境,包括用户会话属性。有关更多信息,请参见`ExternalContext`API Javadocs。 ``` ``` ## 4.5.范围搜索算法 正如本节前面提到的,在一个流作用域中分配变量时,需要引用该作用域。例如: ``` ``` 当只访问其中一个作用域中的变量时,引用该作用域是可选的。例如: ``` ``` 当没有指定作用域时,就像上面使用`booking`一样,使用作用域搜索算法。该算法将在请求、闪存、视图、流和会话范围中查找变量。如果没有找到这样的变量,将抛出一个`EvaluationException`。