Spring MVC -- 表达式于语言(EL) JSP 中EL表达式用法详解 JSP中的九大隐式对象及四个做用域

JSP 2.0最重要的特性之一就是表达式语言(EL),JSP用户能够用它来访问应用程序数据。因为受到ECMAScript和XPath表达式语言的启发,EL也设计成能够轻松地编写免脚本(就是不用在jsp文件中嵌入脚本)的JSP页面。也就是说页面中不使用任何JSP声明、表达式或者scriptlet。html

本篇博客将会介绍如何使用EL表达式在JSP页面中显示数据和对象属性,它涵盖了最新的EL3.0版本技术。java

一 表达式语言简史

JSP 2.0最初是将EL应用在JSP标准标签库(JSTL)1.0规范中。程序员

JSP 1.2程序员将JSTL库导入到他们的应用程序中,就可使用EL。web

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.0EL也没法知足JSF的全部需求,所以为JSF 1.0开发了一款EL的变体,后者这两种语言变体合二为一。express

2013年5月发布了EL 3.0版本,EL再也不是JSP或任何其它技术的一部分,而是一个独立的规范。EL 3.0添加了对lambda表达式的支持,并容许集合操做,其lambda支持不须要Java SE8,Java SE7便可。编程

二 表达式语言的语法

EL表达式以${开头,并以}结束。EL表达式的结构以下:数组

${expression}
#{expression}

例如,表达式x+y,能够写成:浏览器

${x+y}

或者:tomcat

#{x+y}

\${exp}和#{exp}结构都由EL引擎以相同的方式进行计算。然而,当EL未被用做独立引擎而是使用诸如JSF或JSP的底层技术时,该技术能够不一样地解释构造。例如,在JSF中,\${exp}结构用于当即计算,#{expr}结构用于延迟计算(即表达式直到系统须要它的值时,才进行计算)。另外一方面,当即计算的表达式,会在JSP页面编译时同时编译,并在执行JSP页面时被执行。在JSP 2.1和更高版本中,#{exp}表达式只能在接受延迟表达式的标签属性中使用。

两个表达式能够链接在一块儿。对于一系列的表达式,它们的取值将是从左到右进行,计算结构的类型为String,而且链接在一块儿。假设$a+b=8$,$c+d=10$,那么这两个表达式的计算结果将是810:

${a+b}${c+d}

表达式\${a+b}and\${c+d}的取值结果则是8and10。

注意:在EL表达式中的"+"只有数学运算的功能,没有链接符的功能,它会试着将运算符两边的操做数转换为数值类型,进而进行数学加法运算,而后将结果输出。若出现\${"a"+"b"}则会出现异常。若是想将两个字符串链接可使用\${"a"+="b"}。

若是在定制标签的属性值中使用EL表达式,那么该表达式的取值结果字符串将会强制变成该属性须要的类型:

<my:tag someAttribute="${expression}"/>

像\${这样的字符顺序就表示是一个EL表达式的开头,若是须要的只是文本\${,则须要在它前面加一个转义符\\\${。

一、关键字

如下是关键字,它们不能用做标识符:

and eq gt true instanceof
or ne le false empty
not lt ge null div mod

二、[]和.运算符

EL表达式能够返回任意类型的值。若是object是一个带有属性的对象,则能够利用[]或者.运算符来object的属性。[]和.运算符相似;[]是比较规范的形式,.运算符则比较快捷。

为了访问对象的属性,可使用如下任意一种形式:

${object["propertyName"]}
${object.propertyName}

可是,若是propertyName不是有效的Java变量名, 即属性名称中包含一些特殊字符,如. 或 – 等并不是字母或数字的符号,则只能使用[]运算符。

例如,下面这两个EL表达式能够用来访问隐式对象header中的host属性(EL表达式的隐式对象是指不须要new,就可使用的对象,即JSP容器为每一个页面中的开发人员提供的Java对象):

${header["host"]}
${header.host}

可是,要访问accept-language属性,只能使用[]运算符。

若是对象的属性碰巧返回带有属性的另外一个对象,便可以使用[],也能够用.运算符来访问第二个对象的属性。例如隐式对象pageContext是表示当前JSP的PageContext对象,它有request属性,表示HttpServlertRequest。HttpServlertRequest自带servlertPath属性,那么下列几个表达式结果相同,均能得出pageContext中HttpServlertRequest的servlertPath属性:

${pageContext["request"]["servletPath"]}
${pageContext.request["servletPath"]}
${pageContext.request.servletPath}
${pageContext.["request"].servletPath}

要访问HttpSession,可使用如下语法:

${pageContext.session}

例如,如下表达式会得出session标识符:

${pageContext.session.id}

三、自动转变类型

EL 提供方一个方便的功能就是:自动转变类型,咱们来看下面这个范例:

${param.count + 20}

倘若窗体传来count的值为10时,那么上面的结果为30。以前没接触过JSP 的读者可能会认为上面的例子是理所固然的,可是在JSP 1.2 之中不能这样作,缘由是从窗体所传来的值,它们的类型一概是String,因此当你接收以后,必须再将它转为其余类型。如:int、float 等等,而后才能执行一些数学运算,下面是以前的作法:

<%
  String str_count = request.getParameter("count");
  int count = Integer.parseInt(str_count);
  count = count + 20;
%>

因此,注意不要和java的语法(当字符串和数字用“+”连接时会把数字转换为字符串)搞混淆。

四、取值规则

EL表达式的取值是从左到右进行的,对于exp-a[exp-b]形式的表达式,其EL表达式的取值方法以下:

  1. 先计算exp-a获得value-a;
  2. 若是value-a为null,则返回null;
  3. 而后计算exp-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)抛出ArrayIndexOfBoundsException,则返回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方法抛出异常,该表达式就是无效的,不然,该表达式有效。

三 使用EL访问数据

一、访问变量

EL 存取变量数据的方法很简单:例如:

${username}

它的意思是取出某一范围中名称为username的变量。由于咱们并无指定哪个范围的username,因此它的默认值会先从Page 范围找,假如找不到,再依序到Request、Session、Application范围。假如途中找到username,就直接回传,再也不继续找下去,

可是假如所有的范围都没有找到时,就回传null,固然EL表达式还会作出优化,页面上显示空白,而不是打印输出null。

属性范围

EL中的名称

Page

PageScope

Request

RequestScope

Session

SessionScope

Application

ApplicationScope

咱们也能够指定要取出哪个范围的变量:

范例

说明

${pageScope.username}

取出Page范围的username变量

${requestScope.username}

取出Request范围的username变量

${sessionScope.username}

取出Session范围的username变量

${applicationScope.username}

取出Application范围的username变量

其中,pageScope、requestScope、sessionScope和applicationScope都是EL 的隐含对象,由它们的名称能够很容易猜出它们所表明的意思,

例如:${sessionScope.username}是取出Session范围的username 变量。这种写法是比以前JSP的写法容易、简洁许多.:

<%
  String username = session.getAttribute("username");
%>

二、访问JaveBean

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

${beanName["propertyName"]}
${beanName.propertyName}

例如,访问myBean的secret属性,可使用如下表达式:

${myBean.secret}

三、访问List、Array和Map

能够经过索引来访问List和Array,以下表达返回hobbies(Array或List)中的3个元素:

//Lits或Array
${requestScope.hobbies[0]}
${requestScope.hobbies[1]}
${requestScope.hobbies[2]}

能够经过以下方式访问Map:

${map[key]}

例如:

//Map
${requestScope.map["china"]}
${requestScope.map.china}
${{"Canada":"Ottawa","China":"Beijing"}["Canada"]}

四  EL隐式对象

在JSP页面中,能够利用JSP脚原本访问JSP隐式对象。以下:

    <%
        //设置register.jsp注册信息提交内容编码方式 只对表单post提交方式有效  tomcat8之后默认提交内容编码为utf-8
        request.setCharacterEncoding("UTF-8");
        String name = request.getParameter("uname");
        String pwd = request.getParameter("upwd");
        //要求年龄输入必须是数字
        int age = Integer.valueOf(request.getParameter("uage"));
        String[] hobbies = request.getParameterValues("uhobbies");
                    
    %>

可是,在免脚本的JSP页面中(不使用JSP脚本),则不可能访问这些隐式对象。EL容许经过提供一组它本身的隐式对象来访问不一样的对象。EL隐式对象以下表:

隐式对象

类型

说明

pageContext

javax.servlet.jsp.PageContext

表示此JSP的PageContext

pageScope

java.util.Map

取得Page范围的属性名称所对应的值

pageRequest

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()。返回String[]类型的值

initParam

java.util.Map

如同ServletContext.getInitParameter(String name)。返回String类型的值

一、pageContext

pageContext隐式对象表示当前JSP页面的javax.servlet.jsp.PageContext。它包含了全部其它的JSP隐式对象,以下表:

对象 EL中的类型
request javax.servlet.http.HttpServlertRequest
response javax.servlet.http.HttpServlertResponse
out javax.servlet.jsp.JspWriter
session javax.servlet.http.HttpServlertRequest
application javax.servlet.ServletContext
config javax.servlet.ServletConfig
PageContext javax.servlet.jsp.PageContext
page javax.servlet.jsp.HttpJspPage
exception javax.lang.Throwable

例如,能够利用如下任意一个表达式来获取当前的ServlertRequest:

${pageContext.request}
${pageContext["request"]}

而且,还能够利用如下任意一个表单时来获取请求方法:

${pageContext["request"]["method"]}
${pageContext["request"].method}
${pageContext.request["method"]}
${pageContext.request.method}

下表列出${pageContext.request}中一些有用的属性:

属性 说明
characterEncoding 请求的字符编码
contentType 请求的MIME类型
locale 浏览器首先loale
locales 全部locale
protocol HTTP协议,例如:HTTP/1.1
remoteAddr 客户端IP地址
remoteHost 客户端IP地址或主机名
scheme 请求发送方案,HTTP或HTTPS
serverName 服务器主机名
serverPort 服务器端口
secure 请求是否经过安全连接传输

对请求参数的访问比对其它隐式对象更加频繁;所以,这里提供了param和paramValues两个隐式对象。

二、做用于访问对象(EL域对象)

与范围有关的EL隐含对象包含如下四个:pageScope、requestScope、sessionScope 、applicationScope,它们基本上就和JSP的pageContext、request、session和application同样。

不过必须注意的是,这四个隐含对象只能用来取得范围属性值,即JSP中的getAttribute(String name),却不能取得其余相关信息。例如:JSP中的request对象除能够存取属性以外,还能够进行设置属性、请求转发等:

request.setAttribute("name", "郑洋");    
//请求转发        
request.getRequestDispatcher("rq.jsp").forward(request,response);

可是在EL中,它就只能单纯用来取得对应范围的属性值。例如:咱们要在session 中储存一个属性,它的名称为username,在JSP 中使用session.getAttribute("username")来取得username 的值,可是在EL中,则是使用${sessionScope.username}来取得其值的。

注意:若是不指定域对象,则默认根据从小到大的顺序依次取值,即返回pageScope、requestScope、sessionScope、applicationScope中第一个同名的对象。

三、param

隐式对象param用于获取请求参数值(主要是表单数据)。这个对象表示一个包含全部请求参数的Map。例如,要获取userName参数,可使用如下任意一种表达式:

${param.userName}
${param["userName"]}

等价于JSP脚本:

<%
  String username = request.getAttribute("username");
%>

四、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.connnection}

六、headerValues

隐式对象headerValues表示一个包含全部请求标题并以header属性名称做为key的Map。可是与header不一样的是,隐式对象headerValues返回的Map是一个字符串数组。例如,为了获取标题accept-language的第一个值,要使用如下表达式:

${headerValues["accept-language"][0]}

七、cookie

隐式对象cookie能够用来获取一个cookie。这个对象表示当前HttpServlertRequest中全部cookie的值。例如为了获取名为jsessionid的cookie值,要使用如下表达式:

${cookie.jsessionid.value}

为了获取jsessionid的路径值,要使用如下表达式:

${cookie.jsessionid.path}

八、initParam

隐式对象initParam用于获取上下文参数的值。例如,为了获取名为password的上下文参数值,可使用如下表达式:

$[initParam.password}
$[initParam["password"]}

看到这里,你们应该很明确EL表达式只能经过内置对象取值,也就是只读操做,若是想进行写操做的话就让后台代码去完成,毕竟EL表达式仅仅是视图上的输出标签罢了。

五 使用其余EL运算符

除了.和[]运算符,EL还提供了其它运算符:算术运算符、关系预算法、逻辑运算符、条件运算符、以及empty运算符。使用这些运算符,能够进行不一样的运算,可是因为EL的目的是方便免脚本JSP页面的编程,所以,除了关系运算符外,这些EL运算符的用处都颇有限。

一、算术运算符

算术运算符有5种:

  • 加法(+);
  • 减法(-);
  • 乘法(*);
  • 除法(/或div)
  • 取余/取模(%和mod)

除法和取余运算符都有两种形式,与XPath和ECMAScript是一致的。

注意:EL表达式的计算按优先级从高到低、从左到右进行。下列运算符是按优先级递减顺序排列的:

  • */div%mod;
  • +-;

这表示*、/、div、%以及mode运算符的优先级是同级的,+与-的优先级是同级的,但第二组运算符的优先级小于第一组运算符。所以,表达式:

${1+2*3}

的运算结果是7,而不是9。

注意:在EL表达式中的"+"只有数学运算的功能,没有链接符的功能,它会试着将运算符两边的操做数转换为数值类型,进而进行数学加法运算,而后将结果输出。若出现\${"a"+"b"}则会出现异常。若是想将两个字符串链接可使用\${"a"+="b"}。

二、关系运算符

下面是关系运算符列表:

关系运算符

说明

范例

结果

== 或 eq

等于

\${5==5}或\${5eq5}

true

!= 或 ne

不等于

\${5!=5}或\${5ne5}

false

< 或 lt

小于

\${3<5}或\${3lt5}

true

> 或 gt

大于

\${3>5}或\${3gt5}

false

<= 或 le

小于等于

\${3<=5}或\${3le5}

true

>= 或 ge

大于等于

\${3>=5}或\${3ge5}

false

表达式语言不只可在数字与数字之间比较,还可在字符与字符之间比较,字符串的比较是根据其对应UNICODE值来比较大小的。

注意:在使用EL 关系运算符时,不可以写成:

${param.password1} = =${param.password2}

或者

${ ${param.password1 } = = ${param.password2 } }

而应写成

${ param.password1 = =param.password2 }

三、逻辑运算符

下面是逻辑运算符列表:

逻辑运算符

范例

结果

&&或and

交集\${A && B}或\${A and B}

true/false

||或or

并集\${A || B}或\${A or B}

true/false

!或not

非\${! A }或\${not A}

true/false

四、条件运算符

E条件运算符的语法以下:

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

若是X为null,或者说X是一个长度为0的字符串,那么该表达式将返回true。若是X是一个空Map、空数组或者空集合,它也将返回true。不然,将返回false。

六、字符串链接运算符

+=运算符用于练级字符串,例如,如下表达式打印a+b的值:

${a+=b}

七、分号操做符

;操做符用于分割两个表达式。

六 引入静态属性和静态方法

咱们可使用EL表达式引用在任何Java类中定义的静态字段和方法。可是,在JSP页面中引用静态字段或方法以前,必须使用page伪指令导入包或类包。java.lang包是一个例外,由于它是自动导入的。

咱们可使用page指令导入java.time包:

<%@ page import="java.time.*"%> 

或者,导入单个类:

<%@ page import="java.time.LocalDate"%> 

而后,就可使用EL表达式引用LocalDate类的静态now()方法:

Today is ${LocalDate.now()}

七 建立Set、List和Map

使用EL表达式能够动态的建立Set、List和Map。建立一个Set的语法以下:

{comma-delimited-elements}

例如,以下表达式建立一个5个数字的Set:

${{1,2,3,4,5}}

建立一个List语法以下:

[comma-delimited-elements]

例如,以下表达式建立一组花名的List:

${["Aster","Carnation","Rose"]}

最后,建立一个Map的语法为:

[comma-delimited-key-value-entries]

以下为一组国家及其首都的Map:

${{"Cannada":"Ottawa","China":"Beijing","France":"Paeis"}}

八 操做集合

EL 3.0带来了不少新特性。其中一个主要的贡献是操纵集合的能力。你能够经过调用流方法将集合转换为流来使用此功能。

下面展现如何将列表转换为流,假设myList是一个java.util.List:

${myList.stream()}

大部分流的操做会返回另外一个流,由于能够造成链式操做:

${myList.stream().operation-1().operation-2().toList()}

在链式操做的末尾,一般调用toList()方法,以便打印或格式化结构,如下小结介绍了你能够对流执行的一些操做。

一、toList

toList()方法返回一个List,它包含与当前流相同的成员。调用此方法的主要目的是轻松地打印或操做流元素。下面是一个将列表转换为流,并返回列表的示例:

${[100,200,300].stream().toList()}

固然这个例子也没什么用,稍后在接下来的小节中,你将看到更多的例子。

二、toArray

与toList()相似,但返回的是一个Java数组,一样,在数组中呈现元素一般是有用的,由于许多Java方法将数组做为参数。这里是一个toArray()的例子:

${["one","two","three"].stream().toArray()}

与toList()不一样,toArray()不打印元素,所以次,toList()更常用。

三、limit

limit()方法限制流中元素的数量。

名为cities的List包含7个城市:

[Paris, Strasbourg, London, New York, Beijing, Amsterdam, San Francisco]

下面的代码将元素的数量限制为3:

${cities.stream().limit(3).toList()}

执行时,表达式将返回此列表:

[Paris, Strasbourg, London]

若是传递给limit()方法的参数大于元素的数量,则返回全部的元素。

四、sorted

此方法对流中的元素进行排序,例如,这个表达式:

${cities.stream().sorted().toList()}

返回以下排序后的列表:

[Amsterdam, Beijing, London, New York, Paris, San Francisco, Strasbourg]

五、average

此方法返回流中全部元素的平均值。其返回值是一个Optional对象,它可能为null。须要调用get()获取实际值。

此表达式返回4.0:

${[1,3,5,7].stream().average().get()}

六、sum

才方法计算流中全部元素的总和。例如,此表达式返回16:

${[1,3,5,7].stream().sum()}

七、count

此方法返回流中元素的数量。例如,次表达式返回7:

${[1,3,5,7].stream().count()}

八、min

此方法返回流中元素中的最小值。同average()方法同样,其返回值是一个Optional对象,所以你须要调用get()方法来获取实际值。

例如,此表达式的返回值为1;

${[1,3,100,1000].stream().min().get()}

九、max

此方法返回流中元素中的最大值。同average()方法同样,其返回值是一个Optional对象,所以你须要调用get()方法来获取实际值。

例如,此表达式的返回值为1000;

${[1,3,100,1000].stream().max().get()}

十、map

此方法将流中的每个元素映射到另外一个流中的另外一个元素,并返回该流。此方法接受一个lambda表达式。

例如,此映射方法使用lambda表达式x->2*x,这实际上将每一个元素乘2,并将它们返回到新的流中:

${[1,3,5].stream().map(x->2*x).toList()}

返回列表以下:

[2,6,10]

下面是另外一个示例,它将字符映射为大写。

${cities.stream().map(x->x.toUpperCase()).toList()}

它返回如下列表:

[PARIS, STRASBOURG, LONDON, NEW YORK, BEIJING, AMSTERDAM, SAN FRANCISCO]

十一、filter

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

例如,如下表达式测试城市是否以“S”开头,并返回全部的结果:

${cities.stream().filter(x->x.startsWith("S").toList())}

它产生的列表以下所示:

[Strasbourg, San Francisco]

十二、forEach

此方法对流中的全部元素执行操做,它返回void。

例如,此表达式将城市中的全部元素打印到控制台:

${cities.stream().forEach(x->System.out.println(x))}

 部分代码:

    <%        
        List<String> cities= Arrays.asList("Paris","Strasbourg","London","New York","Beijing","Amsterdam","San Francisco");
        out.print(cities);
        //只在当前页面有效
        pageContext.setAttribute("cities", cities);        
    %>
    <br/>
    1:${cities}<br/>
    2:${cities.stream().map(x->x.toUpperCase()).toList()}<br/>
    3:${cities.stream().sorted().toList()}<br/>
    4:${cities.stream().filter(x->x.startsWith("S")).toList()}<br/>
    5:${cities.stream().forEach(x->System.out.println(x))}<br/>

注意:须要使用tag伪指令导包:

<%@ page import="java.util.*" %>

输出:

九 格式化集合

因为EL定义了如何写表达式而不是函数,所以没法直接打印或格式化集合,毕竟,打印和格式化不是EL负责的领域。然而,打印和格式化是两个不能忽视的重要任务。

最简单的方法就是使用forEach()方法,如下代码能够再tomcat 8以上运行:

<ul>
    ${cities.stream().forEach(x->pageContext.out.println("<li>"+=x+="</li>"))}
</ul>

遗憾的是,这在GlassFish4中不起做用,因此forEach()不能通用。

可是咱们可使用如下两种解决方案,虽然不像forEach()那么优雅,可是在全部主要的servlet容器上均可以使用。下面咱们将介绍两种格式化集合的方法。

一、使用HTML注释

该解决方案适用于Java SE7版本以上。

List字符串表示形式以下:

[element-1,element-2,...]

如今,若是我想在HTML中呈现列表元素,须要这么写:

<ul>
    <li>element-1</li>
    <li>element-2</li>
    ...
</ul>

如今,你可能已经注意到了每一个元素必须转向 <li>element-n</li>。那么咱们应该如何作?

咱们还记得map()方法么,它能够将流中的每个元素映射到另外一个流中的另外一个元素,并返回该流。所以咱们能够利用map()方法转换每一个元素。因此,代码能够这么写:

${myList.stream().map(x->"<li>"+=x+="</li>").toList()}

这样,咱们将会获得以下的List:

[<li>element-1</li>,<li>element-2</li>,...]

足够接近,但任然须要删除括号和逗号。遗憾的是,咱们没法控制列表的字符串表示。可是好在咱们可使用HTML注释。

因此,下面这个例子:

<ul>            
    <!-- ${cities.stream().map(x->" -->
    <li>"+=x+="</li>
    <!--").toList()}-->
</ul>    

上面代码${cities.stream().map(x->"<li>"+=x+="</li>").toList()}中,把<li>"+=x+="</li>之外的代码所有使用<!-- -->注释掉,即只保留列表元素,其余EL语法注释掉。

结果以下所示:

<ul>            
    <!-- [ -->
    <li>Paris</li>
    <!--,  -->
    <li>Strasbourg</li>
    <!--,  -->
    <li>London</li>
    <!--,  -->
    <li>New York</li>
    <!--,  -->
    <li>Beijing</li>
    <!--,  -->
    <li>Amsterdam</li>
    <!--,  -->
    <li>San Francisco</li>
    <!--]-->
</ul>    

能够看到这段代码有效的注释掉了括号和逗号。虽然结果看起来有点混论,可是它是有效的HTML,更重要的是,它能工做。下面是页面的显示效果:

二、使用String.join()

这第二个解决方案适用于Java SE8以上版本,该方法之因此有效,由于EL 3.0容许引入静态方法。在Java SE8中,String类新增了一些静态方法,其中一个方法就是join()。String类中有两个join()重载方法。可是这里须要使用到的一个方法以下所示:

public static String join(CharSequence delimiter,Iterable<? extends CharSequence> elements)

此方法返回用指定分隔符链接在一块儿的CharSequence元素组成的字符串。而java.util.Collection接口正好实现了Iterable接口,所以,能够讲Collection传递给join()方法。

例如,下面是如何将列表格式化成HTML有序列表:

<ol>
    ${"<li>"+=String.join("</li><li>",cities)+="</li>"}
</ol> 

此表达式适用于至少有一个元素的集合,若是你可能要处理一个空集合,这里有一个更好的表达式:

<ol>
    ${empty cities?"":"<li>"
            +=String.join("</li><li>",cities.stream().sorted().toList())
            +="</li>"}
</ol> 

十 格式化数字

要格式化数字,你能够利用EL 3.0中容许引用静态方法的能力。String类的format()静态方法能够用来格式化数字。

例如,如下表达式返回带有两个小数点的数字:

${String.format("%-10.2f%n",125.178) }

更多格式化规则能够查阅java.text.DecimalFormat的javadoc文档。

十一 格式化日期

能够经过String.format()来格式化一个date或time。例如:

${d=LocalDate.now().plusDays(2);String.format("%tB %te,%tY%n",d,d,d)}

首先计算LocalDate.now().plusDays(2),并将结果复制给变量d,而后再利用String.format()方法来格式化LocalDate,引用了3次变量d。输出以下:

十二 在JSP 2.0及更高版本中配置EL

有了EL,JavaBean和定制标签,就能够编写免脚本的JSP页面了。JSP2.0及更高的版本中还提供了一个开关,可使全部的JSP页面都禁用脚本。如今,软件架构师能够强制编写免脚本的JSP页面了。

另外一方面,在有些状况下,可能还会须要在应用程序中取消EL。例如,正在使用与JSP 2.0兼容的容器(tomcat),却还没有准备将JSP应用程序升级到JSP 2.0,那么就须要这么作。在这种状况下,能够关闭EL表达式的计算。

注意:最初,JSP 2.0版本才开始支持EL,也就是说JSP 2.0版本以前的应用程序默认是不支持EL的。

一、实现免脚本的JSP页面

为了关闭JSP页面中的脚本元素,要在部署描述符中(web.xml文件)使用jsp-property-group元素以及url-pattern和scripting-invalid两个子元素。url-pattern元素定义禁用脚本要应用的URL样式。下面展现如何将一个应用程序中全部JSP页面的脚本都关闭:

  <jsp-config>
    <jsp-property-group>
        <url-pattern>*.jsp</url-pattern>
        <scripting-invalid>true</scripting-invalid>
    </jsp-property-group>
  </jsp-config>

因此当执行带有JSP脚本的代码时:

    <% 
        out.println("在免脚本jsp页面中,可使用这个jsp脚本么?");
    %>

注意:在部署描述符中只能有一个jsp-config元素。若是已经为禁用EL而定义了一个jsp-property-group,就必须在同一个jsp-config下,为禁用脚本编写jsp-property-group。

二、禁用EL计算

 在有些状况下,好比,当须要在JSP 2.0及更高版本的容器中部署JSP 1.2应用程序(该版本默认不支持EL)时,可能就须要禁用JSP页面中的EL计算了。此时,出现的EL结构,就不会做为EL表达式进行计算。目前有两种方式能够禁用JSP中的EL计算。

(1) 能够将page指令的isELIgnored属性设置为true,像这样:

<%@ page isELIgnored="true"%>

isELIgnored属性的默认值为false,若是想在一个或者几个JSP页面中关闭EL表达式计算,建议使用isELIgnored属性。

(2) 能够在部署描述符中使用jsp-property-group元素。jsp-property-group元素是jsp-config元素的子元素。利用jsp-property-group能够将某些设置应用到应用程序中的一组JSP页面中。

为了利用jsp-property-group元素禁用EL运算,还必须有url-patter和el-ignored两个子元素。url-pattern元素用于定义EL禁用要应用的URL样式。el-ignored元素必须设置为true。

下面举一个例子,展现如何在名为noEl.jsp的jsp页面中禁用EL计算。

  <jsp-config>
    <jsp-property-group>
        <url-pattern>/noEl.jsp</url-pattern>
        <el-ignored>true</el-ignored>
    </jsp-property-group>
  </jsp-config>

此时,noEl.jsp页面<body>内容以下:

<body>
    ${1+3}
</body>

输出以下:

不管是将page指令的isELIgnored属性设置为true,仍是其URL与el-ignored为true的jsp-property-group的URL模式向匹配,都将禁用JSP页面中的EL计算。假设将一个JSP页面中的page指令的isELIgnored属性设置为false,但其URL与在部署描述符中禁用了EL计算的JSP页面的模式匹配,那么该页面的EL计算也将被禁用。

此外,若是使用的是与Servlet2.3及更低版本兼容的部署描述符,那么EL计算已经默认关闭,即便使用的是JSP 2.0及更高版本的容器,也同样。

参考博客

[1]Spring MVC学习指南

[2]JSP 中EL表达式用法详解

[3]JSP中的九大隐式对象及四个做用域

[4]JSP入门及JSP三种脚本

相关文章
相关标签/搜索