JavaWEB(十七)--EL表达式
EL 简介
- EL 全名为 Expression Language,它原本是 JSTL 1.0 为方便存取数据所自定义的语言。当时 EL只能在 JSTL 标签中使用,如下:
<c:out value="${ 3 + 7}">
- 程序执行结果为 10。但是你却不能直接在 JSP 网页中使用:
<p>Hi ! ${ username }</p>
- 到了 JSP 2.0 之后,EL 已经正式纳入成为标准规范之一。因此,只要是支持 Servlet 2.4 / JSP2.0 的 Container,就都可以在 JSP 网页中直接使用 EL 了。
- 除了 JSP 2.0 建议使用 EL 之外,JavaServer Faces( JSR-127 ) 也考虑将 EL 纳入规范,EL 如今已经是一项成熟、标准的技术。
EL 语法
EL 语法很简单,它最大的特点就是使用上很方便。接下来介绍 EL 主要的语法结构:
${sessionScope.user.sex}
- 所有 EL 都是以 ${ 为起始、以} 为结尾的。上述 EL 范例的意思是:从 Session 的范围中,取得用户的性别。假若依照之前 JSP Scriptlet 的写法如下:
User user = (User)session.getAttribute("user");
String sex = user.getSex( );
- 两者相比较之下,可以发现 EL 的语法比传统 JSP Scriptlet 更为方便、简洁。
.与 [ ] 运算符
- EL 提供 . 和 [ ] 两种运算符来存取数据。下列两者所代表的意思是一样的:
${sessionScope.user.sex}等于${sessionScope.user["sex"]}
- . 和 [ ] 也可以同时混合使用,如下:
${sessionScope.shoppingCart[0].price}
回传结果为 shoppingCart 中第一项物品的价格。
- 不过,以下两种情况,两者会有差异
- (1) 当要存取的属性名称中包含一些特殊字符,如 . 或 – 等并非字母或数字的符号,就一定要使用 [ ],例如:
${user.My-Name }
上述是不正确的方式,应当改为:
${user["My-Name"] }
- (2) 我们来考虑下列情况:
${sessionScope.user[data]}
此时,data 是一个变量,假若 data 的值为”sex”时,那上述的例子等于{sessionScope.user.name}。因此,如果要动态取值时,就可以用上述的方法来做,但 . 无法做到动态取值。
- 接下来,我们更详细地来讨论一些情况,首先假设有一个 EL:
${expr-a[expr-b]}
(1) 当 expr-a 的值为 null 时,它会回传 null。
(2) 当 expr-b 的值为 null 时,它会回传 null。
(3) 当 expr-a 的值为一 Map 类型时
● 假若 !value-a.containsKey(value-b)为真,则回传 null。
● 否则回传 value-a.get(value-b)。
(4) 当 expr-a 的值为 List 或 array 类型时:
● 将 value-b 的值强制转型为 int,假若不能转型为 int 时,会产生 error。
● 然 后 , 假 若 value-a.get(value-b) 或 Array.get(value-a, value-b) 产 生
ArrayIndexOutOfBoundsException 或 IndexOutOfBoundsException 时,则回传 null。
● 假若 value-a.get(value-b)或 Array.get(value-a, value-b)产生其他的异常时,则会产生
error。
● 最后都没有任何异常产生时,回传 value-a.get(value-b)或 Array.get(value-a, value-b)。
(5) 当 expr-a 的值为 JavaBean 对象时:
● 将 value-b 的值强制转型为 String。
● 假若 getter 产生异常时,则会产生 error。若没有异常产生时,则回传 getter 的结果。
EL 变量
EL 存取变量数据的方法很简单,例如:${username}。它的意思是取出某一范围中名称为 username的变量。因为我们并没有指定哪一个范围的 username,所以它的默认值会先从 Page 范围找,假如找不到,再依序到 Request、Session、Application 范围。假如途中找到 username,就直接回传,不再继续找下去,但是假如全部的范围都没有找到时,就回传 null;
也可以指定要取出哪一个范围的变量
其中,pageScope、requestScope、sessionScope 和 applicationScope 都是 EL 的隐含对象,由
它们的名称可以很容易猜出它们所代表的意思,例如:${sessionScope.username}是取出 Session
范围的 username 变量。这种写法是不是比之前 JSP 的写法:
String username = (String) session.getAttribute("username");
自动转变类型
EL 除了提供方便存取变量的语法之外,它另外一个方便的功能就是:自动转变类型,看
下面这个范例;
${param.count + 20}
假若窗体传来 count 的值为 10 时,那么上面的结果为 30。之前没接触过 JSP 的读者可能会认为上面的例子是理所当然的,但是在 JSP 1.2 之中不能这样做,原因是从窗体所传来的值,它们的类型一律是 String,所以当你接收之后,必须再将它转为其他类型,如:int、float 等等,然后才能执行一些数学运算,下面是之前的做法
String str_count = request.getParameter("count");
int count = Integer.parseInt(str_count);
count = count + 20;
接下来再详细说明 EL 类型转换的规则:
(1) 将 A 转为 String 类型
● 假若 A 为 String 时:回传 A
● 否则,当 A 为 null 时:回传 ""
● 否则,当 A.toString( )产生异常时:错误!
● 否则,回传 A.toString( )
(2) 将 A 转为 Number 类型的 N
● 假若 A 为 null 或 "" 时:回传 0
● 假若 A 为 Character 时:将 A 转为 new Short((short)a.charValue( ))
● 假若 A 为 Boolean 时:错误!
● 假若 A 为 Number 类型和 N 一样时:回传 A
● 假若 A 为 Number 时:
·假若 N 是 BigInteger 时:
·假若 A 为 BigDecimal 时:回传 A.toBigInteger( )
·否则,回传 BigInteger.valueOf(A.longValue( ))
·假若 N 是 BigDecimal 时:
·假若 A 为 BigInteger 时:回传 A.toBigDecimal( )
·否则,回传 BigDecimal.valueOf(A.doubleValue( ))
·假若 N 为 Byte 时:回传 new Byte(A.byteValue( ))
·假若 N 为 Short 时:回传 new Short(A.shortValue( ))
·假若 N 为 Integer 时:回传 new Integer(A.intValue( ))
·假若 N 为 Long 时:回传 new Long(A.longValue( ))
·假若 N 为 Float 时:回传 new Float(A.floatValue( ))
·假若 N 为 Double 时:回传 new Double(A.doubleValue( ))
·否则,错误!
● 假若 A 为 String 时:
·假若 N 是 BigDecimal 时:
·假若 new BigDecimal(A)产生异常时:错误!
·否则,回传 new BigDecimal(A)
·假若 N 是 BigInteger 时:
·假若 new BigInteger(A)产生异常时:错误!
·否则,回传 new BigInteger(A)
·假若 N.valueOf(A)产生异常时:错误!
·否则,回传 N.valueOf(A)
● 否则,错误!
(3) 将 A 转为 Character 类型
● 假若 A 为 null 或 "" 时:回传 (char)0
● 假若 A 为 Character 时:回传 A
● 假若 A 为 Boolean 时:错误!
● 假若 A 为 Number 时:转换为 Short 后,然后回传 Character
● 假若 A 为 String 时:回传 A.charAt(0)
● 否则,错误!
(4) 将 A 转为 Boolean 类型
● 假若 A 为 null 或 "" 时:回传 false
● 否则,假若 A 为 Boolean 时:回传 A
● 否则,假若 A 为 String,且 Boolean.valueOf(A)没有产生异常时:回传 Boolean.valueOf(A)
● 否则,错误!
EL 保留字
所谓保留字的意思是指变量在命名时,应该避开上述的名字,以免程序编译时发生错误。
EL 隐含对象
- 这 11 个隐含对象(Implicit Object),分成三类:
1. 与范围有关的隐含对象
applicationScope
sessionScope
requestScope
pageScope
2. 与输入有关的隐含对象
param
paramValues
3. 其他隐含对象
cookie
header
headerValues
initParam
pageContext
属性(Attribute)与范围(Scope)
- 与范围有关的 EL 隐含对象包含以下四个:pageScope、 requestScope、 sessionScope 和applicationScope,它们基本上就和 JSP 的 pageContext、request、session 和 application 一样,所以笔者在这里只稍略说明。不过必须注意的是,这四个隐含对象只能用来取得范围属性值,即 JSP中的 getAttribute(String name),却不能取得其他相关信息,例如:JSP 中的 request 对象除可以存取属性之外,还可以取得用户的请求参数或表头信息等等。但是在 EL 中,它就只能单纯用来取得对应范围的属性值,例如:我们要在 session 中储存一个属性,它的名称为 username,在 JSP 中使用session.getAttribute(“username”) 来 取 得 username 的 值 , 但 是 在 EL 中 , 则 是 使 用${sessionScope.username}来取得其值的。接下来分别对这四个隐含对象做简短的说明:
● pageScope
范围和 JSP 的 Page 相同,也就是单单一页 JSP Page 的范围(Scope)。
● requestScope
范围和 JSP 的 Request 相同,requestScope 的范围是指从一个 JSP 网页请求到另一个 JSP 网页
请求之间,随后此属性就会失效。
● sessionScope
范围和 JSP Scope 中的 session 相同,它的属性范围就是用户持续在服务器连接的时间。
● applicationScope
范围和 JSP Scope 中的 application 相同,它的属性范围是从服务器一开始执行服务,到服务
器关闭为止。
与输入有关的隐含对象
- 与输入有关的隐含对象有两个:param 和 paramValues,它们是 EL 中比较特别的隐含对象。一般而言,我们在取得用户的请求参数时,可以利用下列方法:
request.getParameter(String name)
request.getParameterValues(String name)
- 在 EL 中则可以使用 param 和 paramValues 两者来取得数据。
${param.name}
${paramValues.name}
这 里 param 的 功 能 和 request.getParameter(String name) 相 同 , 而 paramValues 和
request.getParameterValues(String name)相同。如果用户填了一个表格,表格名称为 username,则我们就可以使用${param.username}来取得用户填入的值。
其他隐含对象
● cookie
所谓的 cookie 是一个小小的文本文件,它是以 key、value 的方式将 Session Tracking 的内容记录在这个文本文件内,这个文本文件通常存在于浏览器的暂存区内。JSTL 并没有提供设定 cookie 的动作,因为这个动作通常都是后端开发者必须去做的事情,而不是交给前端的开发者。假若我们在 cookie 中设定一个名称为 userCountry 的值,那么可以使用${cookie.userCountry}来取得它。
● header 和 headerValues
header 储存用户浏览器和服务端用来沟通的数据,当用户要求服务端的网页时,会送出一个记载要求信息的标头文件,例如:用户浏览器的版本、用户计算机所设定的区域等其他相关数据。假若要取得用户浏览器的版本,即${header[“User-Agent”]}。另外在鲜少机会下,有可能同一标头名称拥有不同的值,此时必须改为使用 headerValues 来取得这些值。
● initParam就像其他属性一样,我们可以自行设定 web 站台的环境参数(Context),当我们想取得这些参数时,可以使用 initParam 隐含对象去取得它
● pageContext
我们可以使用 ${pageContext}来取得其他有关用户要求或页面的详细信息
EL 算术运算符
EL 算术运算符主要有以下五个
- 依照下列几种情况,详细说明 EL 算术运算符的规则:
(1) A {+ ,- , *} B
● 假若 A 和 B 为 null:回传 (Long)0
● 假若 A 或 B 为 BigDecimal 时,将另一个也转为 BigDecimal,则:
·假若运算符为 + 时:回传 A.add(B)
·假若运算符为 - 时:回传 A.subtract(B)
·假若运算符为 * 时:回传 A.multiply(B)
● 假若 A 或 B 为 Float、Double 或包含 e / E 的字符串时:
·假若 A 或 B 为 BigInteger 时,将另一个转为 BigDecimal,然后依照运算符执行
运算
·否则,将两者皆转为 Double,然后依照运算符执行运算
● 假若 A 或 B 为 BigInteger 时,将另一个也转为 BigInteger,则:
·假若运算符为 + 时:回传 A.add(B)
·假若运算符为 - 时:回传 A.subtract(B)
·假若运算符为 * 时:回传 A.multiply(B)
● 否则,将 A 和 B 皆转为 Long,然后依照运算符执行运算
● 假若运算结果产生异常时,则错误!
(2) A {/ , div} B
● 假若 A 和 B 为 null:回传 (Long)0
● 假若 A 或 B 为 BigDecimal 或 BigInteger 时,皆转为 BigDecimal,然后回传 A.divide(B,
BigDecimal.ROUND_HALF_UP)
● 否则,将 A 和 B 皆转为 Double,然后依照运算符执行运算
● 假若运算结果产生异常时,则错误!
(3) A {% , mod} B
● 假若 A 和 B 为 null:回传 (Long)0
● 假若 A 或 B 为 BigDecimal、Float、Double 或包含 e / E 的字符串时,皆转为 Double,然
后依照运算符执行运算
● 假若 A 或 B 为 BigInteger 时,将另一个转为 BigInteger,则回传 A.remainder(B)
● 否则,将 A 和 B 皆转为 Long,然后依照运算符执行运算
● 假若运算结果产生异常时,则错误!
(4) -A
● 假若 A 为 null:回传 (Long)0
● 假若 A 为 BigDecimal 或 BigInteger 时,回传 A.negate( )
● 假若 A 为 String 时:
·假若 A 包含 e / E 时,将转为 Double,然后依照运算符执行运算
·否则,转为 Long,然后依照运算符执行运算
·假若运算结果产生异常时,则错误!
● 假若 A 为 Byte、Short、Integer、Long、Float 或 Double
·直接依原本类型执行运算
·假若运算结果产生异常时,则 错误!
● 否则,错误!
Tomcat 上的 jsp-examples 中,有一个 EL 算术运算符的范例 basic-arithmetic.jsp。
EL 关系运算符
- EL 关系运算符有以下六个运算符
在使用 EL 关系运算符时,不能够写成:
${param.password1} = = ${param.password2}
或者
${ ${param.password1 } = = ${ param.password2 } }
而应写成
${ param.password1 = = param.password2 }
- 依照下列几种情况,详细说明 EL 关系运算符的规则:
(1) A {<, >, <=, >=, lt, gt, le, ge} B
● 假若 A= = B,运算符为<=, le, >=, ge 时,回传 true,否则回传 false
● 假若 A 为 null 或 B 为 null 时,回传 false
● 假若 A 或 B 为 BigDecimal 时,将另一个转为 BigDecimal,然后回传 A.compareTo(B)的值
● 假若 A 或 B 为 Float、Double 时,皆转为 Double 类型,然后依其运算符运算
● 假若 A 或 B 为 BigInteger 时,将另一个转为 BigInteger,然后回传 A.compareTo(B)的值
● 假若 A 或 B 为 Byte、Short、Character、Integer 或 Long 时,皆转为 Long 类型,然后依其
运算符运算
● 假若 A 或 B 为 String 时,将另一个也转为 String,然后做词汇上的比较
● 假若 A 为 Comparable 时,则:
·假若 A.compareTo(B)产生异常时,则错误!
● 否则,采用 A.compareTo(B) 的比较结果
● 假若 B 为 Comparable 时,则:
· 假若 B.compareTo(A)产生异常时,则错误!
● 否则,采用 A.compareTo(B) 的比较结果
● 否则,错误!
(2) A {= =, !=, eq, ne} B
● 假若 A= = B,依其运算符运算
● 假若 A 为 null 或 B 为 null 时:= = /eq 则回传 false,!= / ne 则回传 true
● 假若 A 或 B 为 BigDecimal 时,将另一个转为 BigDecimal,则:
· 假若运算符为 = = / eq,则 回传 A.equals(B)
· 假若运算符为 != / ne,则 回传 !A.equals(B)
● 假若 A 或 B 为 Float、Double 时,皆转为 Double 类型,然后依其运算符运算
● 假若 A 或 B 为 BigInteger 时,将另一个转为 BigInteger,则:
·假若运算符为 = = / eq,则 回传 A.equals(B)
·假若运算符为 != / ne,则 回传 !A.equals(B)
● 假若 A 或 B 为 Byte、Short、Character、Integer 或 Long 时,皆转为 Long 类型,然后依其
运算符运算
● 假若 A 或 B 为 Boolean 时,将另一个也转为 Boolean,然后依其运算符运算
● 假若 A 或 B 为 String 时,将另一个也转为 String,然后做词汇上的比较
● 否则,假若 A.equals(B)产生异常时,则 错误!
● 否则,然后依其运算符运算,回传 A.equals(B)
EL 逻辑运算符
- EL 逻辑运算符
- 下面举几个例子:
${ param.month = = 7 and param.day = = 14 }
${ param.month = = 7 || param.day = = 14 }
${ not param.choice }
- EL 逻辑运算符的规则很简单:
(1) A {&&, and, || 或 or } B
·将 A 和 B 转为 Boolean,然后依其运算符运算
(2) {!, not}A
·将 A 转为 Boolean,然后依其运算符运算
EL 其他运算符
- EL 除了上述三大类的运算符之外,还有下列几个重要的运算符:
(1) Empty 运算符
(2) 条件运算符
(3) ( ) 括号运算符
Empty 运算符
Empty 运算符主要用来判断值是否为 null 或空的,例如:
${ empty param.name }
- Empty 运算符的规则:
(1) {empty} A
● 假若 A 为 null 时,回传 true
● 否则,假若 A 为空 String 时,回传 true
● 否则,假若 A 为空 Array 时,回传 true
● 否则,假若 A 为空 Map 时,回传 true
● 否则,假若 A 为空 Collection 时,回传 true
● 否则,回传 false
- 条件运算符
- 条件运算符如下:
${ A ? B : C}
意思是说,当 A 为 true 时,执行 B;而 A 为 false 时,则执行 C。
括号运算符
括号运算符主要用来改变执行优先权,例如:${ A * (B+C) }
至于运算符的优先权,如下所示(由高至低,由左至右):
· []、.
· ( )
· - (负)、not、!、empty
· *、/、div、%、mod
· +、- (减)
· <、>、<=、>=、lt、gt、le、ge
· = =、!=、eq、ne
· &&、and
· []、.
· ( )
· - (负)、not、!、empty
· *、/、div、%、mod
· +、- (减)
· <、>、<=、>=、lt、gt、le、ge
· = =、!=、eq、ne
· &&、and