Java EE之表达式语言EL(上)

1.了解表达式语言

表达式语言(EL)用于在不使用脚本、声明或者表达式的情况下,在JSP页面中渲染数据。
EL曾是JSTL 1.0规范(与JSP 1.2)中的一部分,并且只可以用作JSTL标签的特性。
到了JSP 2.0和JSTL1.1,由于EL的流行,它的规范从JSTL规范移动到了JSP规范中,并且在JSP的任何部位都可以使用,不再限制于JSTL标签特性中。
到了Java EE 7,它被移动到了自己的JSR(JSR 341)中,并更新了对Lambda表达式和Java Collections Stream API的支持,这标志着Java统一表达式语言3.0的产生(简称EL3.0)。

1.1 基本语法

JSP转换器必须要能够检测到EL表达式的开始和结束,并能与页面的其他部分区分开,然后正确地解析和执行表达式。
基本的EL语法有两种不同的类型:

  • 立即执行
  • 延迟执行

立即执行
因为JSP从上向下执行,这意味着EL表达式将在JSP引擎发现它,并在继续执行其他页面部分之前执行它。
${expr},其中expr是一个有效的EL表达式。

\${not an EL expression}不是EL表达式(美元符号之前的后斜线将告诉JSP引擎这并不是一个EL表达式),它将被按照字面内容输出到响应中。还可以使用美元符号的XML实体$取代\$,结果是相同的,${not an EL expression}

当然,有时也需要在表达式之前添加后斜线,但仍然希望表达式执行。这时就必须使用后斜线的XML实体,\${EL expression to evaluate}

延迟执行
延迟执行EL主要用于满足JavaServer Faces的需要,#{expr}

添加EL表达式

  • EL表达式不能用再任何指令中。在编译JSP时,指令(<%@ page %><%@ include %><%@ taglib %>)将会被执行,但EL表达式是在稍后渲染JSP时执行。
  • JSP声明<%! %>、脚本<% %>或者表达式<%= %>中的EL表达式也是无效的。

除此之外,EL表达式可以添加到其他任何位置。

  • 将EL表达式内嵌在文本中显示到屏幕, The user will see ${expr} text and will know that ${expr} is good.
  • 表达式还可以用在标准的HTML标签特性中,<input type="text" name="something" value="${expr}" />
  • 在JSP标签特性中也可以使用。
<c:url value="/something/${expr}/${expr}" />
<c:redirect url="${expr}" />

EL表达式可以只是特性值的一部分。

在JavaScript和层叠样式表中也可以包含EL表达式:

<script type="text/javascript" lang="javascript">
	var employeeName = '${expr}';
	var booleanValue = '${expr}';
	var numericValue = '${expr}';
</script>
<style type="text/css">
	span.error{
		color: ${expr};
		background-image: url('/some/place/${expr}.png');
	}
</style>

2.使用EL语法

EL语法是弱类型,并且它包含了许多内建的隐式类型转换。
表达式主要的规则是执行后要产生某个值。不能在表达式中声明变量、执行赋值语句或者不产生结果的操作。
使用EL表达式是为了避免在JSP中使用Java。

在EL3.0规范中可以在表达式中为变量赋值,${x=5}将把5赋给x,并且在页面中渲染出EL表达式中的5。

2.1 EL表达式的关键字

  • empty用于验证某些集合、Map或者数组是否含有值,或者某些字符串是否含有一个或多个字符。如果它们为null或者“空”,那么表达式的结果为真;否则,结果为假,${empty x}
  • 关键字divmod分别对应着/(除法)和%(求余),它们只是数学运算符的替代关键字。如果愿意的话,你仍然可以使用/%
  • 关键字andornot分别对应着Java逻辑运算符&&||!
  • 关键字eqneltgtlege分别对应着Java逻辑运算符==!=<><=>=

2.2 操作符优先级

EL表达式的操作符有优先级,优先级相同的操作符将按照它们出现的顺序,从左向右执行。
操作符的优先级,从上(最高优先级)到下(最低优先级)显示:

[] .
()
一元运算符 - ! not empty
* / div % mod
数学运算符 + -
字符串连接 +=
< lt > gt <= le >= ge
== eq != ne
&& and
|| or
三元运算符 ? :
Lambda表达式 ->
赋值 =
在表达式中同时使用多个表达式 ;

2.2.1 字面量

EL表达式也有字符串字面量,EL中的字符串字面量既可以使用双引号也可以使用单引号。

<c:url value="${'value'}"/>
<c:url value='${"value"}'/>
<c:url value="${\"value\"}"/>
<c:url value='${\'value\'}'/>

如果字符串变量自身已经包含了单引号或双引号,必须将其中一些引号转义。

<c:url value="${'some \"value\"'}"/>
<c:url value='${"some \"value\""}'/>
<c:url value="${'some \'value\''}"/>
<c:url value='${"some \'value\'"}'/>
<c:url value="${\"some 'value'\"}"/>
<c:url value='${\'some "value"\'}'/>

如果可能,最好保持字符串字面量尽可能的简单。

EL表达式中的字符串字面量连接:

The user will see ${expr} text and will ${expr}.
${'The user will see ' += expr += " text and will " += expr += '.'}
${"The user will see " += expr += ' text and will ' += expr += "."}

如果某些对象中的expr结果不是字符串,它将被强制转型为字符串。

EL表达式使数学计算变得更简单,所以所有的类型转换和精度升级都将以隐式的方式完成。
在EL表达式中,只允许使用十进制字面量。

EL并未包含charbyteshort类型的字面量,但可以在必要的时候将其他字面量强制转型为charbyteshort

通过使用EL集合字面量可以构造集合、列表和Map,构造实例时将会使用它们的默认实现。

  • 字面量集合将会构造出HashSet<Object>{1, 2, 'three', 4.00, x}
  • 字面量列表将会构造出ArrayList<Object>[1, 2, 'three', [x, y], {'foo', 'bar'}]
  • 字面量Map将会构造出HashMap<Object, Object>{'one': 1, 2: 'two', 'key': x, 'list': [1, 2, 3] }

构造列表的方法与构造集合的方法基本一致,区别在于列表使用的是方括号,而集合使用的是花括号。
可以通过在一个集合对象中插入另一个集合对象的方式嵌套集合。

对象属性和方法
不可以在EL表达式中访问公开字段。假设现在有一个名为Shirt的类,它包含了一个公开字段size。通过${shirt.size}访问Shirt对象的size字段是行不通的。
现在对Shirt类进行修改,将size字段改为私有的,并为size字段提供getter和setter方法,就可以通过${shirt.size}访问Shirt对象的size字段了。
也可以使用[]操作符访问属性,${shirt["size"]}

通过EL可以在JSP中调用对象的方法,在调用一个需要传入参数并且具有返回值的函数时,方法调用特别有用。

静态字段和方法访问
表达式语言3.0中新增的特性,可以访问JSP类路径中任何类的公开静态字段和公开静态方法。
除非使用的类已经使用JSP page指令导入,否则必须使用完全限定的类名。记录:在JSP中,如同Java一样,所有在java.lang包中的类都已经被隐式地导入了。所以${java.lang.Integer.MAX_VALUE}可以被写为${Integer.MAX_VALUE}。必须要注意的是只可以读取这些字段的值,不能修改它们。
EL表达式还可以调用类的构造函数,它将返回一个该类的实例,可基于该实例进一步访问属性、调用方法或者将它强制转型为字符串用于输出。

${com.wrox.User()}
${com.wrox.User('First', 'Last').firstName}

枚举
从传统上来讲,EL中的枚举将在必要的时候被强制转型为字符串,或者从字符串强制转型为枚举。
例如,现在JSP中有一个局部变量dayOfWeek,它是Java 8新增的日期和时间API中java.time.DayOfWeek枚举的实例,测试dayOfWeek代表是不是星期六:${dayOfWeek == 'SATURDAY'}
变量dayOfWeek将被转换为字符串,但它并不是类型安全的。
到了EL 3.0,可以使用静态字段访问语法实现类型安全的枚举常量引用。毕竟,枚举常量只是他们枚举类型的公共静态的、不可改变的字段。

${dayOfWeek == java.time.DayOfWeek.SATURDAY}

lambda表达式
一个lambda表达式就是一个匿名函数。
EL中的lambda表达式体中可以包含任何对于Java方法来说合法的代码,而EL的lambda表达式体重包含的则是另一个EL表达式。

a -> a + 5
(a, b) -> a + b

使用了lambda表达式的EL表达式:

${(a -> a + 5)(4)}
${((a, b) -> a + b)(4,7)}

定义之后使用的lambda表达式,${v = (a, b) -> a + b; v(3, 15)}(第二个表达式的输出是18)。

lambda表达式作为参数传入到方法调用中,${users.stream().filter(u -> u.lastName == 'Williams' || u.lastName == 'Sanders').toArray()}

集合
EL可以使用点和中括号操作符轻松地访问集合。如何使用操作符取决于使用集合的类型。
访问Map的值的方式相当简单:

${map["username"]}
${map["userId"]}
${map.username}
${map.userId}

访问列表页同样的简单:

${list[0]}
${list[1]}
${list["0"]}
${list['1']}

EL允许使用字符串字面量代替数字用作列表索引,但所使用的字符串必须能够转换为整数。我们推荐使用数字字面量。

集合和队列不能通过EL访问,它们也没有访问方法,只能通过遍历的方式访问它们。

参考:《Java Web高级编程》

posted @ 2018-06-27 10:26  gzhjj  阅读(1309)  评论(0编辑  收藏  举报