EL表达式语言是一种简单的数据访问语言,基本语法格式为"${表达式}",当JSP引擎在解析JSP页面的过程中遇到"${表达式}"这样的字符序列时,JSP引擎就会调用EL引擎来解释执行花括号对({})中的表达式。"${表达式}"中的表达式必须符合EL语法要求,它具有以下一些特点:
EL表达式中可以直接使用属性名来引用存储在各种域范围(page、request、session、application)中的属性,例如,${user}等效于<%=pageContext.findAttribute("user") %>
EL表达式中可以使用${customerBean.address.country}的形式来访问JavaBean对象中的属性对象中的属性,以及可以使用${users[0]}的形式来访问有序集合中的元素
EL表达式中可以执行基本的关系运算、逻辑运算和算术运算,例如,${1 > (4/2)}输出结果为false
EL表达式中可以使用自定义函数来完成一些更复杂的功能,例如,${osc.filter("<img/>")},osc.filter是用户自定义的函数,"<img/>"则是传递给这个自定义函数的参数
EL表达式中可以使用一系列的隐含对象,例如,pageContext、cookie等
在JSP文件中,字符串"${"作为EL表达式的开始标记,所以,如果要输出字面意义的字符串${,需转换为\${。如果在EL表达式内部包含"$"字符或"${"字符串,只需将它们作为一个普通的字符串常量用引号引起来即可,例如,"${"${"}"。
JSP标签的属性值只包含一个单独的EL表达式的情况如下:
<some:tag value="${表达式}" />
JSP标签的属性值同时包含多个EL表达式和多段静态文本的情况如下:
<some:tag value="some ${expr} ${expr} text ${expr}"
这时候,所有的EL表达式被依次计算并转换为字符串类型,然后与静态文本连接成组成一个新的字符串,最后根据类型转换规则将其转换为属性值要求的类型。
EL表达式可以直接使用JSP模板中,如果JSP页面支持EL表达式,JSP引擎将解析和执行EL表达式,并将执行的结果输出到EL表达式所在的位置处。
对于一个单独的JSP页面,可以通过page指令的isELIgnored属性来设置JSP页面是否忽略EL表达式,其格式为:<%@ page isELIgnored="true|false" %>,JSP2.0默认支持EL,如果要使JSP页面忽略EL,设置为true即可。如果想让当前Web应用程序所有JSP页面都忽略EL表达式,则可以在Web应用程序的web.xml文件中添加:
<jsp-property-group> <url-pattern>*.jsp</url-pattern> <el-ignored>true</el-ignored> </jsp-property-group>
如果同时设置了,则以JSP文件内部的定义为准。
在web.xml应用程序的web.xml文件中添加:
<jsp-property-group> <url-pattern>*.jsp</url-pattern> <scripting-invalid>true</scripting-invalid> </jsp-property-group>
对于EL表达式式中出现的标识符,EL引擎首先判断这个标识符是否是一个隐含对象,如果是,则返回相应的隐含对象,否则把这个标识符当作一个域属性来看待,调用pageContext.findAttribute返回这个域属性所对应的对象,不存在则返回null。
如果在某个域中定义的属性的名称正好等于某个EL隐含对象的名称,在EL表达式中引用这个名称时,EL引擎将把它当作隐含对象对待,而不是返回域属性的值。
隐含对象 |
类型 |
说明 |
PageContext |
javax.servlet.ServletContext |
表示此JSP的PageContext |
PageScope |
java.util.Map |
取得Page范围的属性名称所对应的值 |
RequestScope |
java.util.Map |
取得Request范围的属性名称所对应的值 |
sessionScope |
java.util.Map |
取得Session范围的属性名称所对应的值 |
applicationScope |
java.util.Map |
取得Application范围的属性名称所对应的值 |
param |
java.util.Map |
如同ServletRequest.getParameter(String name)。回传String类型的值 |
paramValues |
java.util.Map |
如同ServletRequest.getParameterValues(String name)。回传String[]类型的值 |
header |
java.util.Map |
如同ServletRequest.getHeader(String name)。回传String类型的值 |
headerValues |
java.util.Map |
如同ServletRequest.getHeaders(String name)。回传String[]类型的值 |
cookie |
java.util.Map |
如同HttpServletRequest.getCookies() |
initParam |
java.util.Map |
如同ServletContext.getInitParameter(String name)。回传String类型的值 |
该对象对应于JSP页面的pageContext对象,在EL表达式可以通过该对象来访问request、response、servletContext和servletConfig等对象。
可以使用 ${pageContext}来取得其他有关用户要求或页面的详细信息。下表列出了几个比较常用的部分
Expression |
说明 |
${pageContext.request.queryString} |
取得请求的参数字符串 |
${pageContext.request.requestURL} |
取得请求的URL,但不包括请求之参数字符串,即servlet的HTTP地址。 |
${pageContext.request.contextPath} |
服务的webapplication的名称 |
${pageContext.request.method} |
取得HTTP的方法(GET、POST) |
${pageContext.request.protocol} |
取得使用的协议(HTTP/1.1、HTTP/1.0) |
${pageContext.request.remoteUser} |
取得用户名称 |
${pageContext.request.remoteAddr} |
取得用户的IP地址 |
${pageContext.session.new} |
判断session是否为新的,所谓新的session,表示刚由server产生而client尚未使用 |
${pageContext.session.id} |
取得session的ID |
${pageContext.servletContext.serverInfo} |
取得主机端的服务信息 |
(pageScope、requestScope、sessionScope、applicationScope)
(1)、${pageScope.userName}返回page域中userName属性的值。
(2)、${userName}表示在page、request、session、application这四个作用域内按顺序查找userName属性,直到找到为止。
(param、paramValues)
假设访问JSP页面时传递了两个名称都为productID的参数,参数值分别为"123"和"456",那么,${param.productID}的返回值为"123",表达式${paramValues.productID}的返回是一个字符串数组,数组的第一个元素为"123",可以用${paramValues.productID[0]}来获得,第二个对象可以用表达式${paramValues.productID[1]}来获得。
(header、headerValues)
header隐含对象返回一个请求头字段的单个值,如${header.referer}可以方便地获得referer请求头字段的值。headerValues用于返回一个请求头字段的所有值。
该对象是一个代表所有Cookie信息的Map对象。
<% response.addCookie(new Cookie("userName", "zhangsan")); %> userName的Cookie对象信息:${cookie.userName}<br> userName的名称和值:${cookie.userName.name}、${cookie.userName.value}<br>
第一次访问(没有接收到userName的Cookie信息,所以不会向Web服务器回传名为userName的Cookie信息):
然后刷新:
该对象是一个代表Web应用程序中的所有初始化参数的Map对象,每个初始化参数的值是ServletContext.getInitParameter(String name)方法返回的字符串。初始化参数可以在server.xml或web.xml中指定,具体则略过。
EL表达式中的变量和自定义函数名都称之为标识符。由大小写字母、数字和下划线等组成,不能以数字开头,不能是保留字、不能是隐含对象的标识符。也不能包含单引号、双引号、减号和正斜杠(/)等特殊字符。
and eq gt true instanceof or ne le
false empty not lt ge null div mod
(1)、布尔常量
它的值只有两个:true和false。
(2)、整型常量
取值范围是Java语言中定义的常量Long.MIN_VALUE到Long.MAX_VALUE之间。
(3)、浮点型常量
取值范围是Java语言中定义的常量Double.MIN_VALUE到Double.MAX_VALUE之间。
(4)、字符串常量
单引号或双引号引起来的一连串的字符。如果字符串本身包含单引号或双引号,则需要用反斜杠\进行转义,如果本身包含反斜杠\,则用"\\"表示字面意义。可以看出,反斜杠是一个转义字符。
(5)、Null常量
只有一个值,null。
EL的核心概念就是将变量映射到一个对象上,其中变量不用先定义,而是直接使用。可以直接理解为上文"代表特定域属性集合的隐含对象(pageScope、requestScope、sessionScope、applicationScope)"所描述的用法。
EL提供了如下一些友好的错误处理机制:
(1)、如果域中没有user的属性,${user}输出空字符串,而不是null。
(2)、如果域中没有user的属性,${user.name}也是输出空字符串,而不是报空指针异常。
(3)、上文例子的EL表达式${cookie.userName.name},即使cookie.userName引用的Cookie对象为null,JSP也不会报空指针异常。
(4)、如果变量确实被映射到一个JavaBean对象,如request.setAttribute("now", new java.util.Date()),而这个JavaBean对象不存在EL表达式的变量引用的属性(注意是属性),则抛出异常。
user.java
package com.test; public class User { private String name = "zhangsan"; private String age; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } }
test3.jsp
<!-- ... --> <body> <% User u = new User(); //request.setAttribute("user", u); %> user的信息为:${user }<br> user的名字为:${user.name }<br> user的年龄为:${user.age }<br> <%-- ${user.add } --%> </body> <!-- ... -->
访问test3.jsp:
把request.setAttribute("user", u);注释去掉,再次访问:(这里注意,EL读取属性是根据getter方法,如果User类没有生成getter方法,这里访问也是报下面的异常,同时看到${user}获得的是对象,只是它在域里被称为属性)
把${user.add}注释去掉,再次访问,发现报异常:
(1)、这两个运算符都可以访问各个域属性对象中的属性和隐含对象的属性,这种情况下,效果相同可以互换使用。如:${user.name}等效于${user['name']}。
(2)、[]还可以访问有序集合(实现了List接口的集合)或数组中的指定索引位置的某个元素,如${user[0]}访问集合或数组的第一个元素。这种情况下,只能用[],不能用点运算符。
(3)、[]和.可以结合来使用,如${user[0].['name']}。
(4)、表达式${a[b]},b是一个EL变量,有如下规则:
${a}为null,${a[b]}返回null
${b}为null,${a[b]}返回null
${a}为Map对象,${b}作为关键字,整个表达式返回${a}对象关键字${b}的值;如果不存在关键字${b},则返回null
${a}为List或array对象,首先将${b}的值转换为整数,表达式返回List或array对象指定索引位置的值,如果${b}的值不能转换为整数,则报错。如果转换成功,但小于0或越界,则返回null。如果List或array对象为空,也是报错
${a}的值是一个JavaBean对象,则将${b}的值转换为String类型作为${a}的属性,如果JavaBean对象有${b}这个属性,则返回这个属性的值,否则报错
算术运算符有5种,分别为+、-、*、/(div)和%(mod)。/和div在进行除法运算时,商为小数,如${10/2},运算结果为5.0。
所有比较运算符执行的结果都是布尔类型,包括以下6组:
==(eq)、!=(ne)、<(lt)、>(gt)、<=(le)和>=(ge)
为了避免与JSP页面的标签产生冲突,后四种比较运算符通常采用括号内的形式。并注意如运算符后边是数字,在运算符和数字之间至少要有一个空格${1lt 2},但后边有其他符合则可以不留空格${1lt(1+1)}。
&&、||、!
该运算符用于检测某个变量是否为null或空,结果为布尔类型。如${empty A}:
A指向的对象为null,返回true
A是空字符串,返回true
A是集合或数组,A中没有任何元素,返回true
A是Map对象中一个关键字,如果Map对象为空、Map对象没有指定的关键字或Map对象的关键字对应的值为空,返回true
其他情况下,返回false
A?B:C,将A的计算结果转换为布尔类型,如果为true,执行B并返回B的值,否则执行C并返回C的值。
该运算符用于改变其他运算符的运算优先级。
定义:就是在EL表达式中调用某个Java类的静态方法,这个Java类的静态方法需要在Web应用程序中专门进行配置,才可以被EL表达式调用。
举例:
<body> <% String content = "<meta http-equiv='refresh' content='0; url=http://www.baidu.com' />"; pageContext.setAttribute("content", content); %> ${content } <%-- 1、可以发现浏览器跳转到百度首页,所以要想显示原始内容,应该讲“<”、“>”之类的特殊字符进行HTML编码转换后 再输出给浏览器,而EL本身无法完成这一功能,但是可以在EL调用外部的Java函数来完成。如,将EL修改为: ${zhangsan:trans(content) },表示的是调用zhangsan这个名称空间的一个trans函数, 将content变量作为参赛传递给函数,并输出返回值。 2、剩下的工作是将trans函数映射到某个Java类的一个静态方法上,并将zhangsan这个名称空间映射到一个tld 文件上。 --%> </body>
一般来说,EL自定义函数开发与应用包括以下三个步骤:
(1)编写函数映射到Java类中的静态方法
(2)编写标签描述符tld文件,在tld文件中描述自定义函数
(3)在JSP页面中导入和使用自定义函数
package util; //必须带public修饰符 public class HTMLFilter { //必须是带public修饰符的静态方法 public static String filter(String message) { if(message == null) { return (null); } char content[] = new char[message.length()]; message.getChars(0, message.length(), content, 0); StringBuffer result = new StringBuffer(); for(int i=0; i<content.length; i++) { switch(content[i]) { case '<': result.append("<"); break; case '>': result.append(">"); break; case '&': result.append("&"); break; case '"': result.append("""); break; default: result.append(content[i]); } } return result.toString(); } }
在D:\apache-tomcat-6.0.39\webapps\examples\WEB-INF\jsp2目录下,打开jsp2-example-taglib.tld,将文件复制到func.tld并修改成:
func.tld
<?xml version="1.0" encoding="UTF-8"?> <taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" version="2.0"> <tlib-version>1.0</tlib-version> <short-name>function</short-name> <uri>/WEB-INF/func.tld</uri> <function> <name>trans</name> <function-class>util.HTMLFilter</function-class> <function-signature>java.lang.String filter(java.lang.String)</function-signature> </function> </taglib>
对主要元素简单说明:
<taglib>是TLD文件的根元素,不应进行修改
<uri>用于指定TLD文件的URI,在JSP文件里需要通过这个URI来引入该标签库描述文件
<function>用于描述一个EL自定义函数,其中<name>用于指定函数名,<function-class>用于指定完整的Java类名,<function-signature>用于指定静态方法的签名,即说明方法的返回值类型和各个参数的类型,参数用逗号隔开。一个标签库描述文件可以有多个<function>元素,但其子元素<name>设置的函数名不能相同
编写完标签库描述文件后,需要将它放置到WEB-INF目录下除了classes和lib目录之外的任意子目录中。
编写完标签库描述文件,还必须在JSP页面引入该文件。JSP2.0中定义了两种格式的JSP页面:标准的JSP页面和JSP文档,与此相对应,引入标准标签库文件的方式也有两种:
(1)在标准JSP页面使用taglib指令
taglib指令的语法格式如下:
<%@ taglib prefix="" uri="" %>
prefix属性是一个“引用代号”,作为自定义标签或EL自定义函数的前缀,其值由编码人员指定,不重复即可。uri属性通常是标签库描述文件中定义的<uri>元素的内容。
test.jsp
<%@ taglib prefix="zhangsan" uri="/WEB-INF/func.tld"%> <!-- ... --> <body> <% String content = "<meta http-equiv='refresh' content='0; url=http://www.baidu.com' />"; pageContext.setAttribute("content", content); %> ${zhangsan:trans(content) } </body> <!-- ... -->
访问test.jsp页面:(查看网页源代码,可以发现特殊字符经过了HTML编码转换)
(2)在JSP文档中使用XML名称空间(略过)