写在前面
这一部分简要的总结一下表达式语言EL,JSP用户可以用它来访问应用程序数据。 由于受到ECMAScript和XPath表达式语言的启发, EL也设计成可以轻松地编写免脚本的JSP页面。 也就是说, 页面不使用任何JSP声明、 表达式或者scriptlets。
表达式语言的语法
EL表达式以 ${ 开头, 并以 } 结束。 EL表达式的结构如下:
${expression}
例如表达式x+y,可以写成
${x+y}
注意如果有一系列表达式计算结果,它们的取值将是从左到右进行, 计算结果的类型为String, 并且连接在一起。例如:设a+b=8, c+d=10,那么两个表达式的计算结果将为:810。
${a+b}${c+d}
//output: 810
${a+b}and${c+d}
//output:8and10
如果在定制标签的属性值中使用EL表达式, 那么该表达式的取值结果字符串将会强制变成该属性需要的类型。
像像${这样的字符顺序就表示是一个EL表达式的开头。 如果需要的只是文本${,则需要在它前面加一个转义符, 如 \ ${。
EL关键字
以下是关键字, 它们不能用作标识符:and 、eq 、gt 、true 、instanceof、or 、ne 、le 、false 、empty 、not 、lt 、ge 、null 、div 、mod。
[ ]和.运算符
EL表达式可以返回任意类型的值。 如果EL表达式的结果是一个带有属性的对象, 则可以利用[ ]或者.运算符来访问该属性。 “[ ]”和“.”运算符类似; “[ ]”是比较规范的形式, “.”运算符则比较快捷。
为了访问对象的属性, 可以使用以下任意一种形式:
${object["propertyName"]}
${object.propertyName}
注意:如果propertyName不是有效的Java变量名,只能使用[ ]运算符。 例如:访问accept-language标题, 则只能使用“[ ]”运算符, 因为accept-language不是一个合法的Java变量名。 如果用“.”运算符访问它, 将会导致异常。
如果对象的属性碰巧返回带有属性的另一个对象,则既可以用“[ ]”, 也可以用“.”运算符来访问第二个对象的属性。 例如, 隐式对象pageContext是表示当前JSP的PageContext对象。 它有request属性, 表示HttpServletRequest。 HttpServletRequest带有servletPath属性。 下列几个表达式的结果相同, 均能得出pageContext中HttpServletRequest的servletPath属性值:
${pageContext["request"]["servletPath"]}
${pageContext.request["servletPath"]}
${pageContext.request.servletPath}
${pageContext["request"].servletPath}
EL表达式取值规则
EL表达式的取值是从左到右进行的。 对于expr-a[expr-b]形式的表达式, 其EL表达式的取值方法如下:
- 先计算expr-a得到value-a。
- 如果value-a为null, 则返回null。
- 然后计算expr-b得到value-b。
- 如果value-b为null, 则返回null。
- 如果value-a为java.util.Map, 则会查看value-b是否为Map中的一个key。 若是, 则返回valuea.get(value-b), 若不是, 则返回null。
- 如果value-a为java.util.List, 或者假如它是一个array, 则要进行第7~9步处理:
- 强制value-b为int, 如果强制失败, 则抛出异常。
- 如果value-a.get(value-b)抛出IndexOutOfBoundsException, 或者假如Array.get(valuea, value-b)抛出ArrayIndexOutOfBoundsException, 则返回null。
- 否则, 若value-a是一个List, 则返回value-a.get(value-b); 若value-a是一个array, 则返回Array.get(value-a, value-b)。
- 如果value-a不是一个Map、 List或者 array,那么, value-a必须是一个JavaBean。 在这种情况下, 必须强制value-b为String。 如果value-b是value-a的一个可读属性, 则要调用该属性的getter方法, 从中返回值。如果getter方法抛出异常, 该表达式就是无效的, 否则, 该表达式有效。
EL隐式对象
在JSP页面中, 可以利用JSP脚本来访问JSP隐式对象。 但是, 在免脚本的JSP页面中, 则不可能访问这些隐式对象。 EL允许通过提供一组它自己的隐式对象来访问不同的对象。 EL隐式对象如下所示:
对象 | 描述 |
---|---|
pageContext | 这是当前JSP的javax.servlet.jsp.PageContext |
initParam | 这是一个包含所有环境初始化参数, 并用参数名作为key的Map |
param | 这是一个包含所有请求参数, 并用参数名作为key的Map。 每个key的值就是指定名称的第一个参数值。 因此, 如果两个请求参数同名, 则只有第一个能够利用param获取值。 要想访问同名参数的所有参数值, 就得用params代替 |
paramValues | 这是一个包含所有请求参数, 并用参数名作为key的Map。 每个key的值就是一个字符串数组, 其中包含了指定参数名称的所有参数值。 就算该参数只有一个值, 它也仍然会返回一个带有一个元素的数组 |
header | 这是一个包含请求标题, 并用标题名作为key的Map。 每个key的值就是指定标题名称的第一个标题。 换句话说, 如果一个标题的值不止一个, 则只返回第一个值。 要想获得多个值的标题, 得用headerValues对象代替 |
headerValues | 这是一个包含请求标题, 并用标题名作为key的Map。 每个key的值就是一个字符串数组, 其中包含了指定标题名称的所有参数值。 就算该标题只有一个值, 它也仍然会返回一个带有一个元素的数组 |
cookie | 这是一个包含了当前请求对象中所有Cookie对象的Map。 Cookie名称就是key名称, 并且每个key都映射到一个Cookie对象 |
applicationScope | 这是一个包含了ServletContext对象中所有属性的Map, 并用属性名称作为key |
sessionScope | 这是一个包含了HttpSession对象中所有属性的Map, 并用属性名称作为key |
requestScope | 这是一个Map, 其中包含了当前HttpServletRequest对象中的所有属性, 并用属性名称作为key |
pageScope | 这是一个Map, 其中包含了全页面范围内的所有属性。 属性名称就是Map的key |
pageContext
pageContext对象表示当前JSP页面的javax.servlet.jsp.PageContext。 它包含了所有其他的JSP隐式对象。
对象 | EL中的类型 |
---|---|
request | javax.servlet.http.HttpServletRequest |
response | javax.servlet.http.HttpServletResponse |
Out | javax.servlet.jsp.JspWriter |
session | javax.servlet.http.HttpSession |
application | javax.servlet.ServletContext |
config | javax.servlet.ServletConfig |
PageContext | javax.servlet.jsp.PageContext |
page | javax.servlet.jsp.HttpJspPage |
exception | java.lang.Throwable |
例如:可以利用以下任意一个表达式来获取当前的ServletRequest:
${pageContext.request}
${pageContext["request"]
并且, 还可以利用以下任意一个表达式来获取请求方法:
${pageContext["request"]["method"]}
${pageContext["request"].method}
${pageContext.request["method"]}${pageContext.request.method}
此外、对请求参数的访问比对其他隐式对象更加频繁; 因此, 它提供了param和paramValues两个隐式对象。
initParam
隐式对象initParam用于获取上下文参数的值。 例如, 为了获取名为password的上下文参数值, 可以使用以下表达式:
${initParam.password}
//or
${initParam["password"]}
param
隐式对象param用于获取请求参数值。 这个对象表示一个包含所有请求参数的Map。 例如, 要获取userName参数, 可以使用以下任意一种表达式:
${param.userName}
//or
${param["userName"]}
paramValues
利用隐式对象paramValues可以获取一个请求参数的多个值。 这个对象表示一个包含所有请求参数, 并以参数名称作为key的Map。 每个key的值是一个字符串数组, 其中包含了指定参数名称的所有值。 即使该参数只有一个值, 它也仍然返回一个带有一个元素的数组。 例如, 为了获得selectedOptions参数的第一个值和第二个值, 可以使用以下表达式:
${paramValues.selectedOptions[0]}
${paramValues.selectedOptions[1]}
header
隐式对象header表示一个包含所有请求标题的Map。 为了获取header值, 要利用header名称作为key。例如, 为了获取accept-language这个header值, 可以使用以下表达式:
${header["accept-language"]}
注意这里由于accept-language不是一个有效的Java变量名(书里是这么说的)。如果header名称是一个有效的Java变量名, 如connection, 那么也可以使用“. ”运算符:
${header.connection}
隐式对象headerValues表示一个包含所有请求head, 并以header名称作为key的Map。 但是, 与head不同的是, 隐式对象headerValues返回的Map返回的是一个字符串数组。 例如, 为了获取标题accept-language的第一个值, 要使用以下表达式:
${headerValues["accept-language"][0]}
cookie
隐式对象cookie可以用来获取一个cookie。 这个对象表示当前HttpServletRequest中所有cookie的值。 例如, 为了获取名为jsessionid的cookie值, 要使用以下表达式:
${cookie.jsessionid.value}
为了获取jsessionid cookie的路径值, 要使用以下表达式:
${cookie.jsessionid.path}
applicationScope、 sessionScope、requestScope和pageScope
隐式对象applicationScope用于获取应用程序范围级变量的值。 假如有一个应用程序范围级变量myVar, 就可以利用以下表达式来获取这个属性:
${applicationScope.myVar}
注意, 在servlet/JSP编程中, 有界对象是指在以下对象中作为属性的对象: PageContext、ServletRequest、 HttpSession或者ServletContext。 隐式对象sessionScope、 requestScope和pageScope与applicationScope相似。 但是, 其范围分别为session、request和page。
有界对象也可以通过没有范围的EL表达式获取。在这种情况下, JSP 容器将返回PageContext、ServletRequest、 HttpSession或者ServletContext中第一个同名的对象。 执行顺序是从最小范围(PageContext)到最大范围(ServletContext) 。 例如, 以下表达式将返回today引用的任意范围的对象:
${today}
EL其他运算符
除了“.”和“[]”运算符外, EL还提供了其他运算符:算术运算符、 关系运算符、 逻辑运算符、 条件运算符以及empty运算符。 使用这些运算符时, 可以进行不同的运算。 但是, 由于EL的目的是方便免脚本JSP页面的编程, 因此, 除了关系运算符外, 这些EL运算符的用处都很有限。
算术运算符
算术运算符有5种:
- 加法(+)
- 减法(−)
- 乘法(*)
- 除法(/和div)
- 取余/取模(%和mod)
关于算术运算符的运算优先级,加减法最低,其他高一级,同级按照从左到右计算。
逻辑运算符
- 和(&&和and)
- 或(|| 和or)
- 非(! 和not)
关系运算符
- 等于(==和eq)
- 不等于(!=和ne)
- 大于(>和gt)
- 大于或等于(>=和ge)
- 小于(<和lt)
- 小于或等于(<=和le)
EL关系运算符的语法如下:
${statement? A:B}
例如, 利用下列EL表达式可以测试HttpSession中是否包含名为loggedIn的属性。 如果找到这个属性, 就显示“You have logged in(您已经登录) ”, 否则显示“You have not logged in(您尚未登录) ”:
${(sessionScope.loggedIn==null)? "You have not logged in" :
"You have logged in"}
empty运算符
empty运算符用来检查某一个值是否为null或者empty。 下面是一个empty运算符的使用范例:
${empty X}
如果X为null, 或者说X是一个长度为0的字符串,那么该表达式将返回True。 如果X是一个空Map、 空数组或者空集合, 它也将返回True, 否则, 将返回False。
一个EL表达式应用实例
首先构造两个JavaBean类:Address与Employee。Address类如下:
package app04a;
public class Address {
private String streetName;
private String streetNumber;
private String city;
private String state;
private String zipCode;
private String country;
public String getStreetName() {
return streetName;
}
public void setStreetName(String streetName) {
this.streetName = streetName;
}
public String getStreetNumber() {
return streetNumber;
}
public void setStreetNumber(String streetNumber) {
this.streetNumber = streetNumber;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getZipCode() {
return zipCode;
}
public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
}
Employee类如下:
package app04a;
public class Employee {
private int id;
private String name;
private Address address;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
然后编写EmployeeServlet,将在这个Servlet中完成之前的Address类与Employee类属性值初始化。并向request设置capitals属性值,浏览器转向JSP页面。JSP页面通过EL表达式显示accept-language、session id、employee属性值与capitals属性值。
EmployeeServlet代码如下:
package app04a;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import app04a.Address;
import app04a.Employee;
@WebServlet(urlPatterns = {"/employee"})
public class EmployeeServlet extends HttpServlet {
private static final int serialVersionUID = -5392874;
@Override
public void doGet(HttpServletRequest request,HttpServletResponse response)
throws ServletException, IOException {
Address address = new Address();
address.setStreetName("Rue D'Anjou");
address.setStreetNumber("5090B");
address.setCity("Brossard");
address.setState("Quebec");
address.setZipCode("A1A B2B");
address.setCountry("Canada");
Employee employee = new Employee();
employee.setId(1099);employee.setName("Charles Unjeye");
employee.setAddress(address);
request.setAttribute("employee", employee);
Map<String, String> capitals = new HashMap<String, String>();
capitals.put("China", "Beijing");
capitals.put("Austria", "Vienna");
capitals.put("Australia", "Canberra");
capitals.put("Canada", "Ottawa");
request.setAttribute("capitals", capitals);
RequestDispatcher rd = request.getRequestDispatcher("/employee.jsp");
rd.forward(request, response);
}
}
JSP页面属性值如下:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Employee</title>
</head>
<body>
accept-language: ${header['accept-language']}
<br/>
session id: ${pageContext.session.id}
<br/>
employee: ${requestScope.employee.name}, ${employee.address.city}
<br/>
capital: ${capitals["Canada"]}
</body>
</html>
通过URL调用:
http://localhost:8080/app01a/employee
显示效果如下所示: