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三種腳本

相關文章
相關標籤/搜索