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學習指南