Java Server Pages(JSP)——6. 表达式语言

写在前面

这一部分简要的总结一下表达式语言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表达式的取值方法如下:

  1. 先计算expr-a得到value-a。
  2. 如果value-a为null, 则返回null。
  3. 然后计算expr-b得到value-b。
  4. 如果value-b为null, 则返回null。
  5. 如果value-a为java.util.Map, 则会查看value-b是否为Map中的一个key。 若是, 则返回valuea.get(value-b), 若不是, 则返回null。
  6. 如果value-a为java.util.List, 或者假如它是一个array, 则要进行第7~9步处理:
  7. 强制value-b为int, 如果强制失败, 则抛出异常。
  8. 如果value-a.get(value-b)抛出IndexOutOfBoundsException, 或者假如Array.get(valuea, value-b)抛出ArrayIndexOutOfBoundsException, 则返回null。
  9. 否则, 若value-a是一个List, 则返回value-a.get(value-b); 若value-a是一个array, 则返回Array.get(value-a, value-b)。
  10. 如果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中的类型
requestjavax.servlet.http.HttpServletRequest
responsejavax.servlet.http.HttpServletResponse
Outjavax.servlet.jsp.JspWriter
sessionjavax.servlet.http.HttpSession
applicationjavax.servlet.ServletContext
configjavax.servlet.ServletConfig
PageContextjavax.servlet.jsp.PageContext
pagejavax.servlet.jsp.HttpJspPage
exceptionjava.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

显示效果如下所示:
这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值