期待已久的日子即將到來: 最新版
JavaServer Pages (JSP)
2.0規范即將和其他的J2EE 1.4一同發布。新的JSP版本有一個新的飛躍,采用了新的方式:由于新的語言表達式(Expression Language,以下簡稱為EL)和JSP標準標簽庫(JSP Standard Tag Library ,以下簡稱為JSTL)這兩種新的方式,在頁面中不需要用java,對于開發一般的應用來說,重用代碼變得更加容易。更具體來說,JSP 2.0帶來了以下的優點:
-
首次被JSTL 1.0引入的EL現在被合并到JSP規范中,就像應用template text一樣地使用所有的標準的和定制的組件。
-
新的EL已經被擴展,具備一個函數調用機制,JSTL1.1整合了一系列經常需要使用的函數。
-
新增加的變量和servlet 規范定義的錯誤處理機制被更好地組織起來。通過新增加的變量,JSP error pages 現在可以提供更多的錯誤信息。
-
容器因為更加嚴格的語法檢查可以更容易地找出發生的錯誤。
-
所有的J2EE 1.4規范(包括JSP 2.0 和 Servlet 2.4),為了聲明部署的規則描述而應用了XML schema。這樣的好處之一是你現在可以通過任何順序列出web.xml文件中的描述。JSP 2.0也增加了一些新的配置選項用于部署描述,允許通過全局的配置來代替基于每頁的配置。
-
由于更具伸縮性的規則和新的自定義action element,現在就像編寫XML文件一樣,編寫JSP頁面變得更加容易。
-
定制的標簽庫現在可以開發成一系列的標簽文件(具有JSP元素的文本文件),標簽處理器可以使用新的、簡化的標簽處理器的API。與此同時,新規范加入了一些新的特性,比如:支持在jsp頁面上顯示動態屬性列表和可執行片斷屬性。
在眾多的書籍中,這是頭一個講解JSP 2.0新特性的文章。在這一部分,我們將看到和EL相關的信息,其他的新特性留到后面。在這里我假定讀者已經熟悉JSP 1.2,而且至少聽說過JSTL。
你可能對這本第三版的《JavaServer Pages》感興趣。這本書中,我盡可能在細節上講述所有的內容,而且并不認為你對JSP或者JSTL了解一切。這本書預計在2003年12月 出版,但是你現在可以在
http://www.amazon.com
、Barnes&Noble,或者其他在線書店預訂。
EL(
The Expression Language
)
如果過去使用過JSTL,那么你可能已經熟悉了EL。EL在JSTL 1.0規范中被引入,用來在運行期間對Java表達式中action element屬性賦值提供另一種選擇。當JSTL EL已經非常迅速的流行起來情況下,還是存在一個問題: JSTL EL 表達式僅僅可以與JSTL和custom action一起使用,怎樣才能使用非標準API對EL表達式求值?
JSP 2.0中,JSP容器自己可以理解EL表達式。這使你在所有過去只能應用Java表達式的地方應用EL表達式成為可能,比如:標準和定制action的屬性值,模板文本。
在我們看具體的例子前,讓我們更進一步的看看什么是EL。EL是從JavaScript中獲得啟發的一種語言,XPath(一種用來訪問XML文檔的語言),但是EL在對變量的null值和執行更多數據類型的自動類型轉換的處理上更加寬松。這些新特性對于web應用非常重要,在這些應用中輸入通常通過html表單的request parameter來得到。這些參數可能僅僅在某些請求下才能體現出來,而且瀏覽器經常將request parameter作為文本發送,然而應用程序經常需要把他們作為數字類型、布爾類型(true 或者 false)來使用。通過EL,你根本就很少需要關心缺少某些參數的值或者類型轉換。
一個EL表達式包含變量和操作符。任何存儲在某個JSP作用范圍(如:page、 request、session、application)的bean能被作為一個EL變量來使用。另外,EL支持以下預定義的變量:
變量名稱
|
說明
|
pageScope
|
一個包含所有page scope范圍的變量集合 (a java.util.Map)
|
requestScope
|
一個包含所有request scope范圍的變量集合 (a java.util.Map)
|
sessionScope
|
一個包含所有session scope范圍的變量集合 (a java.util.Map)
|
applicationScope
|
一個包含所有application scope范圍的變量集合 (a java.util.Map)
|
param
|
一個包含所有請求參數的集合 (a java.util.Map),通過每個參數對應一個String值的方式賦值
|
paramValues
|
一個包含所有請求參數的集合 (a java.util.Map),通過每個參數對應一個String數組的方式賦值
|
header
|
一個包含所有請求的頭信息的集合, (a java.util.Map) ,通過每個頭信息對應一個String值的方式賦值
|
headerValues
|
一個包含所有請求的頭信息的集合 (a java.util.Map) ,通過每個頭信息的值都保存在一個String數組的方式賦值
|
cookie
|
一個包含所有請求的 cookie集合 (a java.util.Map), ??通過每一個cookie(javax.servlet.http.Cookie)對應一個cookie值的方式賦值
|
initParam
|
一個包含所有應用程序初始化參數的集合(a java.util.Map) ,通過每個參數分別對應一個String值的方式賦值
|
pageContext
|
一個javax.servlet.jsp.PageContext類的實例, 用來提供訪問不同的請求數據
|
操作符描述了你對變量所期望的操作。如果你之前曾經使用過任何編程語言的話,在EL表達式中所使用的操作符對你來說可能看起來很熟悉。因為它們和那些在大多數語言中所支持的操作符一樣。
Operator
|
Description
|
.
|
訪問一個bean屬性或者 Map entry
|
[]
|
訪問一個數組或者鏈表元素
|
()
|
對子表達式分組,用來改變賦值順序
|
? :
|
條件語句,比如: 條件 ? ifTrue : ifFalse.如果條件為真,表達式值為前者,反之為后者
|
+
|
數學運算符,加操作
|
-
|
數學運算符,減操作或者對一個值取反
|
*
|
數學運算符,乘操作
|
/ or div
|
數學運算符,除操作
|
% or mod
|
數學運算符,模操作(取余)
|
== or eq
|
邏輯運算符,判斷符號左右兩端是否相等,如果相等返回true,否則返回false
|
!= or ne
|
邏輯運算符,判斷符號左右兩端是否不相等,如果不相等返回true,否則返回false
|
< or lt
|
邏輯運算符,判斷符號左邊是否小于右邊,如果小于返回true,否則返回false
|
> or gt
|
邏輯運算符,判斷符號左邊是否大于右邊,如果大于返回true,否則返回false
|
<= or le
|
邏輯運算符,判斷符號左邊是否小于或者等于右邊,如果小于或者等于返回true,否則返回false
|
>= or ge
|
邏輯運算符,判斷符號左邊是否大于或者等于右邊,如果大于或者等于返回true,否則返回false
|
&& or and
|
邏輯運算符,與操作賦。如果左右兩邊同為true返回true,否則返回false
|
|| or or
|
邏輯運算符,或操作賦。如果左右兩邊有任何一邊為true返回true,否則返回false
|
! or not
|
邏輯運算符,非操作賦。如果對true取運算返回false,否則返回true
|
empty
|
用來對一個空變量值進行判斷: null、一個空String、空數組、 空Map、沒有條目的Collection集合
|
func(args)
|
調用方法, func是方法名,args是參數,可以沒有,或者有一個、多個參數.參數間用逗號隔開
|
一個EL表達式可以包含:數字、文本(在單引號或者雙引號之間)、布爾值、null值。
因為一個EL表達式可以出現在靜態文本出現的地方,因此你必須告訴JSP容器它應該被當作一個EL表達式來處理。你可以通過使用定界符來做到這一點。一個EL表達式總是以”${ }”來標記(一個“$”符號和一個左花括號,右花括號)。這里有一個EL表達式,它將一個命名為amount的變量加5:
${amount + 5}
如果你想要將5加到一個bean的property上,可以使用property訪問操作符:
${order.amount + 5}
在當前這個指定的bean或者collection集合中,Property訪問操作符(一個“.“符號)告訴EL去尋找名字為amount的property。
${order['amount'] + 5}
在[]之間的值必須是一個property的名字(就像上面的例子中那樣)或者是一個保存property名字的變量(或者是一個完整的EL子表達式)。
EL表達式可以被用來賦值給任何標準的或者定制的JSP行為屬性(action attribute),這些行為屬性被標記為可以接受動態值(或者請求期間的屬性值,就象它被正式調用一樣):
<c:out value="${order.amount + 5}"/>
在JSP 2.0之前,你不得不使用Java表達式去給一個屬性動態賦值。在過去的很多年中,這已經成為語法混亂的一個普遍根源。
最后,EL表達式可以在頁面中和模板直接混合使用。當你生成HTML并且需要設置一個動態值給一個屬性的時候,這非常方便:
<input name="firstName" value="${customer.firstName}">
JSP 1.2中,你不得不使用JSTL的<c:out>來實現同樣的事情,最后把各種不同類型的元素混合起來,這導致程序理解起來非常的困難:
<input name="firstName"
value="<c:out value="${customer.firstName}"/>" >
?
新JSTL 1.1 Tag Library 標識符
JSTL1.1發布的是一個初級的版本,主要目的是用來整合JSTL和JSP2.0 。最明顯的變化是JSTL1.0 “孿生函數庫”(一組庫用來接受EL表達式,另外一組用來接受JAVA表達式),而它們已經被一組既可以用于EL表達式也可以用于JAVA表達式的函數庫所代替。
在JSTL 1.1中使用以下標識符:
庫
|
URI
|
前綴
|
Core
|
http://java.sun.com/jsp/jstl/core
|
c
|
XML processing
|
http://java.sun.com/jsp/jstl/xml
|
x
|
I18N formatting
|
http://java.sun.com/jsp/jstl/fmt
|
fmt
|
Database access
|
http://java.sun.com/jsp/jstl/sql
|
sql
|
Functions
|
http://java.sun.com/jsp/jstl/functions
|
fn
|
如果你曾經使用過JSTL1.0,你可能會注意到新的標識符和舊的EL庫標試符一模一樣,除了加入了“/jsp path” element。你也可能注意到在JSTL1.1中有一個庫,包含了EL的函數。我們稍后就會看到。
一個新的EL操作符
在JSP頁面中一個非常普遍的需求就是:當某個條件為真時,要在網頁中包含一些文字。在JSP1.2和JSTL1.1中,用具有代表性的<c:if>來實現,但是這樣做非常繁瑣。JSP2.0增加了一個新的條件操作符用于EL,以更加優雅的方式來處理這樣的情況。這個條件操作符存在于很多編程語言中(比如:Java,C,JavaScript),因此你可能以前就見過它。它判斷一個布爾的條件,當條件為真或者假時,分別取不同的結果。
一個能清楚說明它如何工作的例子:
<select name="artist">
<option value="1" ${param.artist == 1 ? 'selected' : ''}>
Vesica Pisces
<option value="2" ${param.artist == 2 ? 'selected' : ''}>
Cortical Control
<option value="3" ${param.artist == 3 ? 'selected' : ''}>
Vida Vierra
</select>
在這里,我使用了EL表達式和條件操作符來選擇是否包含 html 中的 “selected”屬性,只有符合條件的 “option” 才被添加 “selected” 屬性。如果條件(param.artist==1)為真時,前面的“selected” 才被添加到網頁中;否則就添加后面的(在這里是空字符串 ‘’)到頁面中。
EL函數
當EL從JSTL規范中移到JSP規范中,它使用了一個如何進行函數調用的技巧。這個EL函數語法非常簡單:方法名,緊接著在圓括號中有一組參數:
<%@ taglib prefix="fn"
uri="http://java.sun.com/jsp/jstl/functions" %>
${fn:length(myCollection)}
這是一個屬于標簽庫中的函數,并且函數名字在頁面中所包含的前綴要指定taglib庫。在這個例子中,我使用了前綴fn,這是JSTL function庫默認的前綴。
標簽庫描述符(Tag Library Descriptor,TLD)將函數名稱映射到一個由JAVA實現的靜態方法中:
<function>
<description>
Returns the number of items in a collection or the number of characters in a string.
</description>
<name>length</name>
<function-class>
org.apache.taglibs.standard.functions.Functions
</function-class>
<function-signature>
int length(java.lang.Object)
</function-signature>
</function>
在這里最有趣的element是<function-signature>。它包含一個函數返回類型的聲明,靜態的方法的名字,在圓括號中聲明該方法所有參數的類型(可以沒有參數或者有多個,參數間用逗號間隔開)。返回值類型和參數類型必須是java的原始類型(Object)或者是其他合法類型。
這個靜態方法 length()在Jakarta Taglibs標準庫中用類似于下面的代碼實現的:
public static int length(Object obj)
throws JspTagException {
if (obj == null)
return 0;
if (obj instanceof String)
return ((String)obj).length();
if (obj instanceof Collection)
return ((Collection)obj).size();
if (obj instanceof Map)
return ((Map)obj).size();
int count = 0;
if (obj instanceof Iterator) {
Iterator iter = (Iterator) obj;
count = 0;
while (iter.hasNext()) {
count++;
iter.next();
}
return count;
}
if (obj instanceof Enumeration) {
Enumeration enum = (Enumeration) obj;
count = 0;
while (enum.hasMoreElements()) {
count++;
enum.nextElement();
}
return count;
}
try {
count = Array.getLength(obj);
return count;
} catch (IllegalArgumentException ex) {}
throw new JspTagException("Unsupported type"));
}
就像你所看到的,在那里沒有什么出奇的地方。它是一個常規的靜態方法,這個函數中通過對運行期中的參數類別的判斷,找出參數的長度。
除了在這個方法中使用的length()方法,JSTL1.1標簽庫還包含了許多其它經常使用的函數:
函數
|
描述
|
fn:contains(string, substring)
|
如果參數string中包含參數substring,返回true
|
fn:containsIgnoreCase(string, substring)
|
如果參數string中包含參數substring(忽略大小寫),返回true
|
fn:endsWith(string, suffix)
|
如果參數 string 以參數suffix結尾,返回true
|
fn:escapeXml(string)
|
將有特殊意義的XML (和HTML)轉換為對應的XML character entity code,并返回
|
fn:indexOf(string, substring)
|
返回參數substring在參數string中第一次出現的位置
|
fn:join(array, separator)
|
將一個給定的數組array用給定的間隔符separator串在一起,組成一個新的字符串并返回。
|
fn:length(item)
|
返回參數item中包含元素的數量。參數Item類型是數組、collection或者String。如果是String類型,返回值是String中的字符數。
|
fn:replace(string, before, after)
|
返回一個String對象。用參數after字符串替換參數string中所有出現參數before字符串的地方,并返回替換后的結果
|
fn:split(string, separator)
|
返回一個數組,以參數separator 為分割符分割參數string,分割后的每一部分就是數組的一個元素
|
fn:startsWith(string, prefix)
|
如果參數string以參數prefix開頭,返回true
|
fn:substring(string, begin, end)
|
返回參數string部分字符串, 從參數begin開始到參數end位置,包括end位置的字符
|
fn:substringAfter(string, substring)
|
返回參數substring在參數string中后面的那一部分字符串
|
fn:substringBefore(string, substring)
|
返回參數substring在參數string中前面的那一部分字符串
|
fn:toLowerCase(string)
|
將參數string所有的字符變為小寫,并將其返回
|
fn:toUpperCase(string)
|
將參數string所有的字符變為大寫,并將其返回
|
fn:trim(string)
|
去除參數string 首尾的空格,并將其返回
|
結束語:
引自:http://www.oreilly.com.cn/news/jsppart1.php?c=java
在這篇文章中,我從EL講到JSTL1.1規范、EL新特色和JSTL 1.1函數庫。接下來的部分我將要告訴你:關于JSP error-page的改進和增強; jsp:id 屬性帶來的益處;新的配置屬性描述符;JSP2.0如何使JSP操作XML變得更加容易;自定義標簽庫的新特性。
如果你想要嘗試JSP2.0的新特性,我建議你使用Apache Tomcat 5。它是最早實現了JSP新規范的容器之一。在公布最終版本的JSP 2.0規范之前,一個被標記為“stable”版本的Tomcat是不能被發布的。但是最新的beta版已經被證實是非常穩定的,不要理會beta版的標記。Tomcat 5在
the Jakarta Project site
可以下載。
?