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}
。- 关键字
div
和mod
分别对应着/
(除法)和%
(求余),它们只是数学运算符的替代关键字。如果愿意的话,你仍然可以使用/
和%
。 - 关键字
and
、or
和not
分别对应着Java逻辑运算符&&
、||
和!
。 - 关键字
eq
、ne
、lt
、gt
、le
和ge
分别对应着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并未包含char
、byte
和short
类型的字面量,但可以在必要的时候将其他字面量强制转型为char
、byte
和short
。
通过使用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高级编程》