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表达式的取值方法以下:
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 存取变量数据的方法很简单:例如:
${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"); %>
利用.或[]运算符,均可以访问bean的属性,其结构以下:
${beanName["propertyName"]}
${beanName.propertyName}
例如,访问myBean的secret属性,可使用如下表达式:
${myBean.secret}
能够经过索引来访问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"]}
在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隐式对象表示当前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隐含对象包含如下四个: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用于获取请求参数值(主要是表单数据)。这个对象表示一个包含全部请求参数的Map。例如,要获取userName参数,可使用如下任意一种表达式:
${param.userName}
${param["userName"]}
等价于JSP脚本:
<% String username = request.getAttribute("username"); %>
利用隐式对象能够获取一个请求参数的多个值。这个对象表示一个包含全部请求参数,并以参数名称做为key的Map,每一个key的值时一个字符串数组,其中包含了指定参数名称的全部值。即便该参数只有一个值,它也仍然返回一个带有一个元素的数组。例如,为了获取selectedOptions参数的第一个值和第二个值,可使用如下表达式:
${paramValues.selectedOptions[0]}
${paramValues.selectedOptions[1]}
隐式对象header表示一个包含全部请求标题的Map,主要包含如下属性:
为了获取header值,要利用header属性名称做为key。例如,为了获取accept-language这个header,可使用如下表达式:
${header["accept-language"]}
若是header名称是一个有效的Java变量,如connection,那么也可使用.运算符:
${header.connnection}
隐式对象headerValues表示一个包含全部请求标题并以header属性名称做为key的Map。可是与header不一样的是,隐式对象headerValues返回的Map是一个字符串数组。例如,为了获取标题accept-language的第一个值,要使用如下表达式:
${headerValues["accept-language"][0]}
隐式对象cookie能够用来获取一个cookie。这个对象表示当前HttpServlertRequest中全部cookie的值。例如为了获取名为jsessionid的cookie值,要使用如下表达式:
${cookie.jsessionid.value}
为了获取jsessionid的路径值,要使用如下表达式:
${cookie.jsessionid.path}
隐式对象initParam用于获取上下文参数的值。例如,为了获取名为password的上下文参数值,可使用如下表达式:
$[initParam.password}
$[initParam["password"]}
看到这里,你们应该很明确EL表达式只能经过内置对象取值,也就是只读操做,若是想进行写操做的话就让后台代码去完成,毕竟EL表达式仅仅是视图上的输出标签罢了。
除了.和[]运算符,EL还提供了其它运算符:算术运算符、关系预算法、逻辑运算符、条件运算符、以及empty运算符。使用这些运算符,能够进行不一样的运算,可是因为EL的目的是方便免脚本JSP页面的编程,所以,除了关系运算符外,这些EL运算符的用处都颇有限。
算术运算符有5种:
除法和取余运算符都有两种形式,与XPath和ECMAScript是一致的。
注意:EL表达式的计算按优先级从高到低、从左到右进行。下列运算符是按优先级递减顺序排列的:
这表示*、/、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 运算符主要用来判断值是否为空(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()}
使用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()方法返回一个List,它包含与当前流相同的成员。调用此方法的主要目的是轻松地打印或操做流元素。下面是一个将列表转换为流,并返回列表的示例:
${[100,200,300].stream().toList()}
固然这个例子也没什么用,稍后在接下来的小节中,你将看到更多的例子。
与toList()相似,但返回的是一个Java数组,一样,在数组中呈现元素一般是有用的,由于许多Java方法将数组做为参数。这里是一个toArray()的例子:
${["one","two","three"].stream().toArray()}
与toList()不一样,toArray()不打印元素,所以次,toList()更常用。
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]
此方法返回流中全部元素的平均值。其返回值是一个Optional对象,它可能为null。须要调用get()获取实际值。
此表达式返回4.0:
${[1,3,5,7].stream().average().get()}
才方法计算流中全部元素的总和。例如,此表达式返回16:
${[1,3,5,7].stream().sum()}
此方法返回流中元素的数量。例如,次表达式返回7:
${[1,3,5,7].stream().count()}
此方法返回流中元素中的最小值。同average()方法同样,其返回值是一个Optional对象,所以你须要调用get()方法来获取实际值。
例如,此表达式的返回值为1;
${[1,3,100,1000].stream().min().get()}
此方法返回流中元素中的最大值。同average()方法同样,其返回值是一个Optional对象,所以你须要调用get()方法来获取实际值。
例如,此表达式的返回值为1000;
${[1,3,100,1000].stream().max().get()}
此方法将流中的每个元素映射到另外一个流中的另外一个元素,并返回该流。此方法接受一个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]
此方法根据lambda表达式过滤流中的全部元素,并返回包含结果的新流。
例如,如下表达式测试城市是否以“S”开头,并返回全部的结果:
${cities.stream().filter(x->x.startsWith("S").toList())}
它产生的列表以下所示:
[Strasbourg, San Francisco]
此方法对流中的全部元素执行操做,它返回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容器上均可以使用。下面咱们将介绍两种格式化集合的方法。
该解决方案适用于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,更重要的是,它能工做。下面是页面的显示效果:
这第二个解决方案适用于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。输出以下:
有了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页面中的脚本元素,要在部署描述符中(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。
在有些状况下,好比,当须要在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学习指南