天天看点

【720科技SpringMVC】---20180420 SpringMVC 表达式语言

内容关键词: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:

利用. 或[]运算符,都可以访问 bean 的属性,其结构如下:

${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

   pageContext:pageContext 对象表示当前 JSP 页面的 javax.servlet.jsp.PageContext。

对象 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:

隐式对象 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 表达式。

  filt:此方法根据 lambda 表达式过滤流中的所有元素,并返回包含结果的新流。

  forEach:此方法对流中的所有元素执行操作。它返回 void。

格式化集合:

  使用 HTML 注释、使用 String.join()

格式化数字:要格式化数字,你可以再次利用EL 3.0 允许引用静态方法的能力。String 类的 format 静态方法可以用来格式化数字。

格式化日期:可以通过 String.format()来格式化一个 date 或 time。

相关参考材料:Spring MVC学习指南(第2版)