[综合]表达式语言(1)[复制链接] 满世界飞 版主 1楼 发表于 2011-3-26 19:57 | 只看该作者 | 倒序浏览 | 打印 EL简介 EL使得页面开发人员能够使用简单的表达式动态的访问来自JavaBean组件的数据。例如,下面的条件标签中的test属性是由EL表达是提供的,这个表达式将0与来自session作用域中的名为cart的bean中的items的数目做比较。 <c:if test="${sessionScope.cart.numberOfItems > 0}"> ... </c:if> 复制代码 JavaServer Faces技术使用EL获得以下的功能: 对表达式延迟求值或立即求值设置和获取数据的能力调用方法的能力 更多怎样在JavaServer Faces应用程序中使用EL的信息本教程后面的“使用EL来引用后台bean”一节。 总而言之,EL提供了使用简单的表达式来执行以下任务的方法: 动态的读取保存在JavaBean组件、各种数据结构或隐式的对象中的应用程序数据动态写数据(如用户在表单中的输入)到JavaBean组件调用任意的静态和公用方法动态执行算术运算 EL还用来指定客户标签属性会接受的下面这些类型的表达式: 立即求值表达式或延迟求值表达式:立即求值表达式会马上被底层技术(如JavaServer Faces)求值。延迟求值表达式会在以后被底层技术使用EL来求值。值表达式或方法表达式:值表达式引用一个数据,方法表达式调用一个方法Rvalue表达式或Lvalue表达式:rvalue表达式只能读一个值,lvalue表达式可以读取值,也可以将值写入外部数据。最后,EL提供了一个可插拔的API,开发人员可以实现该接口来处理目前还不被EL支持的表达式。 立即求值和延迟求值语法 EL既支持表达式的立即求值,也支持表达式的延迟求值。立即求值的意思是表达式的值在页面第一次呈现的时候就被计算而且值被返回。延迟求值的意思是使用表达式语言的技术(如JSF)可以使用自己的机制来在页面生命周期的其他时候计算表达式的值。 要立即求值的表达式使用语法${},要延迟求值的表达式使用语法#{}。由于多阶段生命周期的特性,JavaServer Faces技术大多数使用的是延迟求值表达式。在生命周期中,组件事件的处理、数据的验证以及其他任务要按照特定的顺序执行。因此JavaServer Faces实现必须将表达式的求值延迟到生命周期中某个合适的点来执行。 其他使用EL的技术可能有其他的使用延迟求值的原因。 立即求值 使用语法${expr}的表达式会被立即求值。这种表达式只能够在模板文本中使用或作为那些可以接受运行时表达式的标签属性的值使用。 下面的例子中显示了一个标签,该标签的的value属性引用了一个立即求值的表达式,该表达式从session作用域中一个名为cart的bean中获取总价。 <fmt:formatNumber value="${sessionScope.cart.total}"/> 复制代码 JavaServer Faces实现会计算表达式${sessionScope.cart.total},转换它,并将返回结果传递给标签处理器。 立即求值的表达式都是只读的值表达式。上面的例子中的表达式不能用来设置总价,只能从cart bean中获取总价。 延迟求值 延迟求值表达式的语法是#{expr},延迟求值的表达式会在页面生命周期的其他的阶段被求值,在哪个阶段求值是由使用该表达式的具体的技术决定的。在JavaServer Faces技术中,JSF的控制器可以在生命周期的不同阶段来计算表达式的值,取决于表达式在页面上是怎样使用的。 下面的例子中显示了一个JavaServer Faces的inputText标签,这个标签显示了一个文本框组件,用户可以在其中输入值。这个inputText标签的value属性引用了一个延迟求值的表达式,该表达式指向名为customer的bean的name属性: <h:inputText id="name" value="#{customer.name}"/> 复制代码 在对包含该标签的页面的首次请求中,JavaServer Faces实现会在生命周期的render-response阶段计算表达式的值。在这个阶段,这个表达式经济访问customer的name属性,和立即求值一样。 在后续的请求中,JavaServer Faces的思想会在生命周期的不同阶段来计算该表达式,会从request中获取值,验证取得的值并将值设置到customer中。 如果在例子中展示的,延迟求值的表达式可以是: 可以读和写的值表达式方法表达式 下一节中我们会介绍值表达式和方法表达式。 值表达式和方法表达式 EL定义了两种类型的表达式:值表达式和方法表达式。值表达式可以获取和设置值。方法表达式指向可以调用并返回值的方法。 值表达式 值表达式可以进一步划分为右边值表达式(rvalue expression)和左边值表达式(lvalue expression)。Rvalues表达式可以读取数据,但是不能写数据。Lvalue既可以读数据也可以写数据。所有使用${}来立即求值的表达式都是rvalue表达式。使用#{}来延迟求值的表达式既可以作为rvalue表达式也可以作为lvalue表达式。看看下面两个表达式: ${customer.name} #{customer.name} 复制代码 第一个表达式使用的是立即求值的语法,第二个使用的是延迟求值的语法。第一个表达式会访问name属性,获取该属性的值,将值添加到response中,最后呈现在页面上。第二个表达式也可以完成同样的功能,但是如果使用该标签的技术允许,标签的处理器可能将对该表达式的求值推迟到页面生命周期的其他时候。 在JavaServer Faces技术中,如果是对页面的第一次请求,第二个表达式会被理解求值。在这种情况向,这个表达式就是作为一个rvalue表达式。在后续的对该页面的请求中,这个表达式可以用来将用户的输入设置给name属性。在这种情况下,这个表达式就是作为lvalue表达式。 使用值表达式引用对象 Rvalue表达式和lvalue表达式都可以引用以下的对象及其属性: JavaBean组件集合(Collections)Java SE枚举类型(enumerated types)隐式对象 要引用这些对象,在表达式中需要使用对象的名字作为变量。下面的表达式引用了一个名为customer的后台bean(JavaBean组件): ${customer} 复制代码 Web容器会PageContext.findAttribute(String)的行为来查找并计算出现在表达式中的变量的值,这里String是变量的名称。例如,当计算表达式${customer}的时候,容器会在页面、请求(requset)、会话(session)、和应用程序等作用域中查找customer并返回其值。如果customer没有找到,则返回null。 开发人员可以使用客户的EL resolver来改变求变量的值的方式。例如,开发人员可以提供一个EL resolver来拦截名为customer的对象,这样${customer}表达式返回的就是EL resolver中的值。 要在表达式中引用enum常量,直接使用字符串文本。例如,有如下的Enum类: public enum Suit {hearts, spades, diamonds, clubs} 复制代码 要在表达式中引用Suit常量Suit.hears,可以直接使用字符串"hearts"。例如在下面的表达式中,mySuit变量是一个Suit的实例,"hearts"会先被转换成Suite.hearts然后再和mySuit进行比较: ${mySuite == "hearts"} 复制代码 使用值表达式引用对象的属性 要引用一个bean或enum实例的属性、集合中的元素或隐式对象的属性,可以使用[]标记。 要引用customer bean的name属性,可以使用表达式${customer.name}或${customer["name"]}。在括号内的内容是要引用的属性的名字的字符串。字符串可以用双引号或者单引号。标记.和[]可以结合使用,如${customer.address["street"]}。 也可以使用这种方式来引用enum常量的属性,但是Enum类的属性必须符合JavaBeans组件的命名规范。也就是说属性必须要有名为getProperty的访问方法,这里Property是要引用的属性的名称。 例如,对于封装了银河系中星球的名称并包含一个获取星球质量的方法的Enum类,我们可以使用下面的表达式来引用Enum类Planet的getMass方法: ${myPlanet.mass} 复制代码 如果要访问数组或list中的某个元素,那么必须要使用可以转换为整数的字符串或者在[]中直接使用整数而不要引号。下面的例子会获取数组或list中同的一个元素(假设socks可以转换成整数): ${customer.orders[1]} ${customer.orders.socks} 复制代码 相对的,要访问Map中的元素,可以使用字符串作为key,不是强制要求。如 ${customer.orders["socks"]} 复制代码 一个rvalue表达式也可以直接是一个值,而不是对象,如算术运算的结果或文字值,例如: ${"literal"} ${customer.age + 20} ${true} ${57} 复制代码 EL定义了以下的文字值: 布尔类型:true和false整数:和Java规则一样浮点数:和Java规则一样字符串:使用引号(单引号或双引号),使用\作为逃逸符。Null:null 还可以写表达式对enum常量进行计算,例如,有如下的Enum类 public enum Suit {club,diamond,heart,spade} 复制代码 在声明了一个名为mySuit的enum常量后,可以通过如下的代码测试mySuit是否是spade: ${mySuit == "spade"} 复制代码 当解析这个表达式的时候,EL会先调用Enum类的valueOf方法,如下 mySuit.valueOf(Suit.class, "spade"} 复制代码 什么时候使用值表达式 使用${}的值表达式可以用于 静态文本任意可以接受表达式的标签属性,无论是标准标签还是客户标签 静态文本中的值表达式会被计算并插入到当前的输出。项目是一个嵌入在静态文本中的表达式的例子: <some:tag> some text ${expr} some text </some:tag> 复制代码 如果静态文本出现在标签体中,注意如果标签体被声明为tagdependent,那么表达式不会被求值。 Lvalue表达式只能够用在接受lvalue表达式的标签属性中。 一个可以接受rvalue表达式或lvalue表达式的标签属性可以如下来设置: 只有一个表达式的 <some:tag value="${expr}"/> <another:tag value="#{expr}"/> 复制代码 这些表达式会被求值,结果被转换成属性所期望的类型。 被文本分隔或包围的一个或多个表达式 <some:tag value="some${expr}${expr}text${expr}"/> <another:tag value="some#{expr}#{expr}text#{expr}"/> 复制代码 这种类型的表达式叫做组合表达式,组合表达式会从左到右进行求值。嵌入在组合表达式中的每个表达式都会被求值并转换成字符串,最后和其他的文本连接在一起,最后形成的字符串会转换成属性所期望的类型。 只有文本 <some:tag value= "sometext"/> 复制代码 这种表达式叫做文本表达式。这种情况下,这个字符串会被转换成属性所期望的类型。文本值表达式有特殊的语法规则,更多的信息见“文本表达式一节”。当标签的属性是enum类型的时候,属性所使用的表达式必须是文本表达式。例如,标签的属性可以使用表达式"hearts"表示Suit.hearts。文本会被转换为Suit,标签的属性得到的值是Suit.hearts。 所有用来设置属性的值的表达式都会在一个期望类型的环境下求值。如果表达式的值和期望的类型不匹配,就会执行转换。例如,如果一个属性期望的类型是float,提供的表达式是${1.2E4},如下的转换会被执行 Float.valueOf("1.2E4").floagValue() 复制代码 完整的类型转换的规则见JavaServer Pages 2.2 Expression Language specification文档的第1.18节,可以从http://jsp.org/aboutJava/communityprocess/final/jsr245/获取该文档。 方法表达式 EL的另外一个特性技术支持延迟的方法表达式。方法表达式可以用来调用一个bean的任意的可以返回结果的public的方法。 在JavaServer Page技术中,组件标签在页面上显示一个组件。组件标签使用方法表达式来调用方法来执行某些处理。这些方法对于处理组件产生的事件或严重组件数据都是必须的,例如下面的例子: <h:form> <h:inputText id="name" value="#{customer.name}" validator="#{customer.validateName}"/> <h:commandButton id="submit" action="#{customer.submit}" /> </h:form> 复制代码 上面的inputText标签会显示一个文本输入框,该标签的validator属性引用了一个名为cutomer的bean的vaildateName的方法。因为方法能够被在生命周期的不同阶段调用,方法表达式必须使用延迟求值的语法。 和lvalue的表达式一样,方法表达式也可以使用.和[]运算符。例如,#{object.method}和#{object["method"]。在[]之中的文字会被转换成字符串,该字符串用来匹配方法的名称。当匹配的方法找到后,该方法会被调用或该方法的信息会被返回。 方法表达式只能以以下的方式应用在标签的属性中: 只有一个表达式,如<some:tag value="#{bean.method}"/>,这里bean是一个JavaBean组件,method是该bean的一个方法。这个表达式会被传递给tag处理器,给表达式所代表的方法以后会被调用。只有文本,如<some:tag value="sometext"/>。方法表达式支持文本主要是为了支持JavaServer Faces技术中的action属性。当这个方法表达式引用的方法被调用的时候,方法返回一个字符串文本,这个字符串会被转换成标签描述中所期望的类型。 参数化方法调用 EL提供了对参数化方法调用的支持。调用方法的时候可以使用参数,而不是必须使用静态的EL。 运算符.和[]都可以用来调用带参数的方法,语法如下: expr-a[expr-b](parameters) expr-a.identifier-b(parameters) 复制代码 在第一个表达式语法中,表达式expr-a被求值并代表一个bean对象。表达式expr-b被求值并转换为一个字符串,该字符串代表expr-a所代表的bean的方法。在第二个表达式语法中,expr-a被求值并代表一个bean对象,identifier-b是一个字符串,该字符串代表bean的方法。括号中的parameters是方法调用的参数。参数可以是0个到多个值或表达式,用逗号隔开。 值表达式和方法表达式都支持参数。下面的例子是对guessnumber应用程序中的一个标签的修改,使用了一个随机数字而不是用户的输入来作为方法调用的参数: <h:inputText value="#{userNumberBean.userNumber(’5’)}"> 复制代码 上面的例子例子中我们使用的是一个值表达式。让我们看看下面的方法表达式的例子, <h:commandButton action="#{trader.buy}" value="buy"/> 复制代码 EL表达式trader.buy会调用trader的buy方法。我们可以将上面的tag修改成传递参数的形式,如下: <h:commandButton action="#{trader.buy(’SOMESTOCK’)}" value="buy"/> 复制代码 我们传递了一个字符串´SOMESTOCK´作为参数到buy方法. 更多关于更新的EL的信息见https://uel.dev.java.net.