内容关键词:EL 表达式、JSP 页面、表达式语言语法、【】和.运算符、取值规则、访问java bean、el隐式对象、使用其他el运算符、引用静态属性和静态方法、创建set list 和map、访问列表元素和map条目、操作集合
格式化集合、使用html注解、格式化数字、格式化日期
知识来源: 720科技(张森鹏)、Spring MVC学习指南(第2版)
JSP 2.0 最重要的特性之一就是表达式语言(EL),JSP 用户可以用它来访问应用程序数据。由于受到 ECMAScript 和 XPath 表达式语言的启发,EL 也设计成可以轻松地编写免脚本的 JSP 页面。也就是说,页面不使用任何 JSP 声明、表达式或者 scriptlet。
表达式语言简史:JSP 2.0 最初是将 EL 应用在 JSP 标准标签库(JSTL)1.0 规范中。JSP 1.2 程序员将标准库导入到他们的应用程序中,就可以使用EL。JSP 2.0 及其更高版本的用户即使没有 JSTL, 也能使用 EL,但在许多应用程序中,还是需要 JSTL 的,因为它里面还包含了与 EL 无关的其他标签。
JSP 2.1 和JSP 2.2 中的 EL 要将JSP 2.0 中的EL 与 JSF(JavaServer Faces)中定义的 EL 统一起来。JSF 是在 Java 中快速构建 Web 应用程序的框架,并且是构建在 JSP 1.2 之上。由于JSP 1.2 中缺乏整合式的表达式语言,并且JSP 2.0 EL 也无法满足JSF 的所有需求,因此为JSF 1.0 开发出了一款 EL 的变体。后来这两种语言变体合二为一。
2013 年 5 月发布了EL 3.0 版本(JSR 341),EL 不再是 JSP 或任何其他技术的一部分,而是一个独立的规范。EL 3.0 添加了对 lambda 表达式的支持,并允许集合操作。其 lambda 支持不需要 Java SE 8,Java SE 7 即可。
表达式语言的语法:
EL 表达式以 ${ 开头,并以 } 结束。EL 表达式的结构如下:
${expression} #{expression}
例如,表达式 x+y,可以写成:
${x+y}
或
#{x+y}
两个表达式可以连接在一起。对于一系列的表达式,它们的取值将是从左到右进行,计算结果的类型为 String,并且连接在一起。假如 a+b 等于 8,c+d 等于 10,那么这两个表达式的计算结果将是 810:
${a+b}${c+d}
表达式${a+b}and${c+d}的取值结果则是 8and10。
如果在定制标签的属性值中使用 EL 表达式,那么该表达式的取值结果字符串将会强制变成该属性需要的类型:
<my:tag someAttribute="${expression}"/>
像${这样的字符顺序就表示是一个 EL 表达式的开头。如果需要的只是文本${,则需要在它前面加一个转义符,如\${。
关键字:
以下是关键字,它们不能用作标识符:
and eq gt trueinstanceof
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}
要访问 HttpSession,可以使用以下语法:
${pageContext.session}
例如,以下表达式会得出 session 标识符。
${pageContext.session.id}
取值规则:
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。若是,则返回 value-a.get(value-b),若不是,则返回 null。
(6)如果 value-a 为 java.util.List,或者假如它是一个 array,则要进行以下处理: a.强制 value-b 为 int,如果强制失败,则抛出异常。
b.如果 value-a.get(value-b)抛出 IndexOutOfBoundsException,或者假如 Array.get (value-a, value-b)抛出 ArrayIndexOutOfBoundsException,则返回 null。
c.否则,若 value-a 是个 List,则返回 value-a.get(value-b);若 value-a 是个 array, 则返回 Array.get(value-a, value-b)。
(7)如果 value-a 不是一个 Map、List 或者 array,那么,value-a 必须是一个 JavaBean。在这种情况下,必须强制 value-b 为 String。如果 value-b 是 value-a 的一个可读属性,则要调用该属性的 getter 方法,从中返回值。如果 getter 方法抛出异常,该表达式就是无效的,否则, 该表达式有效。
访问JavaBean:
${beanName["propertyName"]}
${beanName.propertyName}
例如,访问 myBean 的 secret 属性,可以使用以下表达式:
${myBean.secret}
如果该属性是一个带属性的对象,那么同样也可以利用.或[]运算符来访问第二个对象的该属性。假如该属性是一个 Map、List 或者 array,则可以利用 访问 Map 值或 List 成员或 array 元素的同样规则。
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 |
对象 | 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}
pageContext.request中一些有用的属性
属性 | 说明 |
characterEncoding | 请求的字符编码 |
contentType | 请求的 MIME 类型 |
locale | 浏览器首先 locale |
locales | 所有 locale |
protocol | HTTP 协议,例如:HTTP/1.1 |
remoteAddr | 客户端 IP 地址 |
remoteHost | 客户端 IP 地址或主机名 |
scheme | 请求发送方案,HTTP 或 HTTPS |
serverName | 服务器主机名 |
serverPort | 服务器端口 |
secure | 请求是否通过安全链接传输 |
隐式对象 initParam用于获取上下文参数的值。例如,为了获取名为 password 的上下文参数值,可以使用以下表达式:
${initParam.password}
或者
${initParam["password"]
param:
隐式对象 param 用于获取请求参数值。这个对象表示一个包含所有请求参数的 Map。例如,要获取 userName 参数,可以使用以下任意一种表达式:
${param.userName}
${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"]}
如果 header 名称是一个有效的Java 变量名,如 connection,那么也可以使用.运算符:
${header.connection}
headerValues:隐式对象 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)。
除法和取余运算符都有两种形式,与 XPath 和 ECMAScript 是一致的。
注意,EL 表达式的计算按优先级从高到低、从左到右进行。下列运算符是按优先级递减顺序排列的:
*/div%mod
+-
这表示*、/、div、%以及 mod 运算符的优先级相同,+与−的优先级相同,但第二组运算符的优先级小于第一组运算符。因此,表达式
${1+2*3}
的运算结果是 7,而不是 9。
关系运算符:面是关系运算符列表:
等于(==和 eq)。
不等于(!=和 ne)。
大于(>和 gt)。
大于或等于(>=和 ge)。
小于(<和 lt)。
小于或等于(<=和 le)。
例如,表达式${3==4}返回 False,${“b”<“d”}则返回 True。
逻辑运算符:下面是逻辑运算符列表:
和(&&和 and)。
或(|| 和 or)。
非(!和 not)。
条件运算符:EL 条件运算符的语法如下:
${statement? A:B}
如果 statement 的计算结果为 True,那么该表达式的输出结果就是 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。
字符串连接运算符:+ =运算符用于连接字符串。 例如,以下表达式打印 a + b 的值。
$ {a + = b}
分号操作符:操作符用于分隔两个表达式。
引用静态属性和静态方法:您可以引用在任何 Java 类中定义的静态字段和方法。 但是,在可以在 JSP 页面中引用静态字段或方法之前,必须使用 page 伪指令导入类或类包。 java.lang 包是一个例外,因为它是自动导入的。
创建Set、List 和Map:可以动态的创建 Set、List 和 Map。创建一个 Set 的语法如下:
{ comma-delimited-elements }
访问列表元素和 Map 条目:可以通过索引来访问 List,如下表达返回 Cities 的第一个元素
${cities[0]}
可以通过如下方式访问 Map
${map[key]}
操作集合:
toList:toList 方法返回一个 List,它包含与当前流相同的成员。调用此方法的主要目的是轻松地打印或操作流元素。
toArray:与 toList 类似,但返回一个 Java 数组。同样,在数组中呈现元素通常是有用的,因为许多Java 方法将数组作为参数。
limit:limit方法限制流中元素的数量。如果传递给 limit方法的参数大于元素的数量,则返回所有元素。
sort:此方法对流中的元素进行排序。
average:此方法返回流中所有元素的平均值。其返回值是一个 Optional 对象,它可能为 null。需要调用 get()获取实际值。
sum:此方法计算流中所有元素的总和。
count:此方法返回流中元素的数量。
min:此方法返回流的元素中的最小值。同 average 方法一样,其返回值是一个Optional 对象,因此你需要调用 get 方法来获取实际值。
max:此方法返回流的元素中的最大值。同 average 方法一样,其返回值是一个 Optional 对象, 因此你需要调用 get 方法来获取实际值。
map:此方法将流中的每个元素映射到另一个流中的另一个元素,并返回该流。此方法接受一个 lambda 表达式。
使用 HTML 注释、使用 String.join()
格式化数字:要格式化数字,你可以再次利用EL 3.0 允许引用静态方法的能力。String 类的 format 静态方法可以用来格式化数字。
格式化日期:可以通过 String.format()来格式化一个 date 或 time。
相关参考材料:Spring MVC学习指南(第2版)