JSP 2.0: The New Deal, Part 1
作者: Hans Bergsten, 《JavaServer Pages, 3rd Edition》11/05/2003
期待已久的日子即將到來: 最新版JavaServer Pages (JSP)2.0規(guī)范即將和其他的J2EE 1.4一同發(fā)布,。新的JSP版本有一個(gè)新的飛躍,,采用了新的方式:由于新的語言表達(dá)式(Expression Language,,以下簡稱為EL)和JSP標(biāo)準(zhǔn)標(biāo)簽庫(JSP Standard Tag Library ,以下簡稱為JSTL)這兩種新的方式,,在頁面中不需要用java,對于開發(fā)一般的應(yīng)用來說,,重用代碼變得更加容易,。更具體來說,JSP 2.0帶來了以下的優(yōu)點(diǎn):
首次被JSTL 1.0引入的EL現(xiàn)在被合并到JSP規(guī)范中,,就像應(yīng)用template text一樣地使用所有的標(biāo)準(zhǔn)的和定制的組件,。 新的EL已經(jīng)被擴(kuò)展,,具備一個(gè)函數(shù)調(diào)用機(jī)制,,JSTL1.1整合了一系列經(jīng)常需要使用的函數(shù),。 新增加的變量和servlet 規(guī)范定義的錯(cuò)誤處理機(jī)制被更好地組織起來,。通過新增加的變量,,JSP error pages 現(xiàn)在可以提供更多的錯(cuò)誤信息,。 容器因?yàn)楦訃?yán)格的語法檢查可以更容易地找出發(fā)生的錯(cuò)誤,。 所有的J2EE 1.4規(guī)范(包括JSP 2.0 和 Servlet 2.4),為了聲明部署的規(guī)則描述而應(yīng)用了XML schema,。這樣的好處之一是你現(xiàn)在可以通過任何順序列出web.xml文件中的描述,。JSP 2.0也增加了一些新的配置選項(xiàng)用于部署描述,允許通過全局的配置來代替基于每頁的配置,。 由于更具伸縮性的規(guī)則和新的自定義action element,,現(xiàn)在就像編寫XML文件一樣,編寫JSP頁面變得更加容易,。 定制的標(biāo)簽庫現(xiàn)在可以開發(fā)成一系列的標(biāo)簽文件(具有JSP元素的文本文件),,標(biāo)簽處理器可以使用新的、簡化的標(biāo)簽處理器的API,。與此同時(shí),,新規(guī)范加入了一些新的特性,比如:支持在jsp頁面上顯示動(dòng)態(tài)屬性列表和可執(zhí)行片斷屬性,。 在眾多的書籍中,,這是頭一個(gè)講解JSP 2.0新特性的文章。在這一部分,,我們將看到和EL相關(guān)的信息,,其他的新特性留到后面。在這里我假定讀者已經(jīng)熟悉JSP 1.2,,而且至少聽說過JSTL,。
你可能對這本第三版的《JavaServer Pages》感興趣。這本書中,,我盡可能在細(xì)節(jié)上講述所有的內(nèi)容,,而且并不認(rèn)為你對JSP或者JSTL了解一切。這本書預(yù)計(jì)在2003年12月 出版,,但是你現(xiàn)在可以在http://www.amazon.com,、Barnes&Noble,或者其他在線書店預(yù)訂,。
EL(The Expression Language)
如果過去使用過JSTL,,那么你可能已經(jīng)熟悉了EL。EL在JSTL 1.0規(guī)范中被引入,,用來在運(yùn)行期間對Java表達(dá)式中action element屬性賦值提供另一種選擇,。當(dāng)JSTL EL已經(jīng)非常迅速的流行起來情況下,還是存在一個(gè)問題: JSTL EL 表達(dá)式僅僅可以與JSTL和custom action一起使用,,怎樣才能使用非標(biāo)準(zhǔn)API對EL表達(dá)式求值,?
JSP 2.0中,JSP容器自己可以理解EL表達(dá)式。這使你在所有過去只能應(yīng)用Java表達(dá)式的地方應(yīng)用EL表達(dá)式成為可能,,比如:標(biāo)準(zhǔn)和定制action的屬性值,,模板文本,。
在我們看具體的例子前,讓我們更進(jìn)一步的看看什么是EL,。EL是從JavaScript中獲得啟發(fā)的一種語言,,XPath(一種用來訪問XML文檔的語言),但是EL在對變量的null值和執(zhí)行更多數(shù)據(jù)類型的自動(dòng)類型轉(zhuǎn)換的處理上更加寬松,。這些新特性對于web應(yīng)用非常重要,,在這些應(yīng)用中輸入通常通過html表單的request parameter來得到。這些參數(shù)可能僅僅在某些請求下才能體現(xiàn)出來,,而且瀏覽器經(jīng)常將request parameter作為文本發(fā)送,,然而應(yīng)用程序經(jīng)常需要把他們作為數(shù)字類型、布爾類型(true 或者 false)來使用,。通過EL,,你根本就很少需要關(guān)心缺少某些參數(shù)的值或者類型轉(zhuǎn)換。
一個(gè)EL表達(dá)式包含變量和操作符,。任何存儲(chǔ)在某個(gè)JSP作用范圍(如:page,、 request、session,、application)的bean能被作為一個(gè)EL變量來使用,。另外,EL支持以下預(yù)定義的變量:
變量名稱 說明
pageScope 一個(gè)包含所有page scope范圍的變量集合 (a java.util.Map)
requestScope 一個(gè)包含所有request scope范圍的變量集合 (a java.util.Map)
sessionScope 一個(gè)包含所有session scope范圍的變量集合 (a java.util.Map)
applicationScope 一個(gè)包含所有application scope范圍的變量集合 (a java.util.Map)
param 一個(gè)包含所有請求參數(shù)的集合 (a java.util.Map),,通過每個(gè)參數(shù)對應(yīng)一個(gè)String值的方式賦值
paramValues 一個(gè)包含所有請求參數(shù)的集合 (a java.util.Map),,通過每個(gè)參數(shù)對應(yīng)一個(gè)String數(shù)組的方式賦值
header 一個(gè)包含所有請求的頭信息的集合, (a java.util.Map) ,通過每個(gè)頭信息對應(yīng)一個(gè)String值的方式賦值
headerValues 一個(gè)包含所有請求的頭信息的集合 (a java.util.Map) ,,通過每個(gè)頭信息的值都保存在一個(gè)String數(shù)組的方式賦值
cookie 一個(gè)包含所有請求的 cookie集合 (a java.util.Map),, 通過每一個(gè)cookie(javax.servlet.http.Cookie)對應(yīng)一個(gè)cookie值的方式賦值
initParam 一個(gè)包含所有應(yīng)用程序初始化參數(shù)的集合(a java.util.Map) ,通過每個(gè)參數(shù)分別對應(yīng)一個(gè)String值的方式賦值
pageContext 一個(gè)javax.servlet.jsp.PageContext類的實(shí)例, 用來提供訪問不同的請求數(shù)據(jù)
操作符描述了你對變量所期望的操作,。如果你之前曾經(jīng)使用過任何編程語言的話,在EL表達(dá)式中所使用的操作符對你來說可能看起來很熟悉,。因?yàn)樗鼈兒湍切┰诖蠖鄶?shù)語言中所支持的操作符一樣,。
Operator Description
. 訪問一個(gè)bean屬性或者 Map entry
[] 訪問一個(gè)數(shù)組或者鏈表元素
() 對子表達(dá)式分組,用來改變賦值順序
: 條件語句,,比如: 條件 ? ifTrue : ifFalse.如果條件為真,,表達(dá)式值為前者,反之為后者
+ 數(shù)學(xué)運(yùn)算符,,加操作
- 數(shù)學(xué)運(yùn)算符,,減操作或者對一個(gè)值取反
* 數(shù)學(xué)運(yùn)算符,乘操作
/ or div 數(shù)學(xué)運(yùn)算符,,除操作
% or mod 數(shù)學(xué)運(yùn)算符,,模操作(取余)
== or eq 邏輯運(yùn)算符,,判斷符號(hào)左右兩端是否相等,如果相等返回true,,否則返回false
!= or ne 邏輯運(yùn)算符,,判斷符號(hào)左右兩端是否不相等,如果不相等返回true,,否則返回false
< or lt 邏輯運(yùn)算符,,判斷符號(hào)左邊是否小于右邊,如果小于返回true,,否則返回false
> or gt 邏輯運(yùn)算符,,判斷符號(hào)左邊是否大于右邊,如果大于返回true,,否則返回false
<= or le 邏輯運(yùn)算符,,判斷符號(hào)左邊是否小于或者等于右邊,如果小于或者等于返回true,,否則返回false
>= or ge 邏輯運(yùn)算符,,判斷符號(hào)左邊是否大于或者等于右邊,如果大于或者等于返回true,,否則返回false
&& or and 邏輯運(yùn)算符,,與操作賦。如果左右兩邊同為true返回true,,否則返回false
|| or or 邏輯運(yùn)算符,,或操作賦。如果左右兩邊有任何一邊為true返回true,,否則返回false
! or not 邏輯運(yùn)算符,,非操作賦。如果對true取運(yùn)算返回false,,否則返回true
empty 用來對一個(gè)空變量值進(jìn)行判斷: null,、一個(gè)空String、空數(shù)組,、 空Map,、沒有條目的Collection集合
func(args) 調(diào)用方法, func是方法名,args是參數(shù),,可以沒有,,或者有一個(gè)、多個(gè)參數(shù).參數(shù)間用逗號(hào)隔開
一個(gè)EL表達(dá)式可以包含:數(shù)字,、文本(在單引號(hào)或者雙引號(hào)之間),、布爾值、null值。
因?yàn)橐粋€(gè)EL表達(dá)式可以出現(xiàn)在靜態(tài)文本出現(xiàn)的地方,,因此你必須告訴JSP容器它應(yīng)該被當(dāng)作一個(gè)EL表達(dá)式來處理,。你可以通過使用定界符來做到這一點(diǎn)。一個(gè)EL表達(dá)式總是以”${ }”來標(biāo)記(一個(gè)“$”符號(hào)和一個(gè)左花括號(hào),右花括號(hào)),。這里有一個(gè)EL表達(dá)式,,它將一個(gè)命名為amount的變量加5:
${amount + 5}
如果你想要將5加到一個(gè)bean的property上,可以使用property訪問操作符:
${order.amount + 5}
在當(dāng)前這個(gè)指定的bean或者collection集合中,,Property訪問操作符(一個(gè)“.“符號(hào))告訴EL去尋找名字為amount的property,。
${order[‘a(chǎn)mount‘] + 5}
在[]之間的值必須是一個(gè)property的名字(就像上面的例子中那樣)或者是一個(gè)保存property名字的變量(或者是一個(gè)完整的EL子表達(dá)式)。
EL表達(dá)式可以被用來賦值給任何標(biāo)準(zhǔn)的或者定制的JSP行為屬性(action attribute),,這些行為屬性被標(biāo)記為可以接受動(dòng)態(tài)值(或者請求期間的屬性值,,就象它被正式調(diào)用一樣):
<c:out value="${order.amount + 5}"/>
在JSP 2.0之前,你不得不使用Java表達(dá)式去給一個(gè)屬性動(dòng)態(tài)賦值,。在過去的很多年中,,這已經(jīng)成為語法混亂的一個(gè)普遍根源。
最后,,EL表達(dá)式可以在頁面中和模板直接混合使用,。當(dāng)你生成HTML并且需要設(shè)置一個(gè)動(dòng)態(tài)值給一個(gè)屬性的時(shí)候,這非常方便:
<input name="firstName" value="${customer.firstName}">
JSP 1.2中,,你不得不使用JSTL的<c:out>來實(shí)現(xiàn)同樣的事情,,最后把各種不同類型的元素混合起來,這導(dǎo)致程序理解起來非常的困難:
<input name="firstName"
value="<c:out value="${customer.firstName}"/>" >
新JSTL 1.1 Tag Library 標(biāo)識(shí)符 JSTL1.1發(fā)布的是一個(gè)初級的版本,,主要目的是用來整合JSTL和JSP2.0 ,。最明顯的變化是JSTL1.0 “孿生函數(shù)庫”(一組庫用來接受EL表達(dá)式,另外一組用來接受JAVA表達(dá)式),,而它們已經(jīng)被一組既可以用于EL表達(dá)式也可以用于JAVA表達(dá)式的函數(shù)庫所代替,。
在JSTL 1.1中使用以下標(biāo)識(shí)符:
庫 URI 前綴
Core http://java./jsp/jstl/core c
XML processing http://java./jsp/jstl/xml x
I18N formatting http://java./jsp/jstl/fmt fmt
Database access http://java./jsp/jstl/sql sql
Functions http://java./jsp/jstl/functions fn
如果你曾經(jīng)使用過JSTL1.0,你可能會(huì)注意到新的標(biāo)識(shí)符和舊的EL庫標(biāo)試符一模一樣,,除了加入了“/jsp path” element,。你也可能注意到在JSTL1.1中有一個(gè)庫,包含了EL的函數(shù),。我們稍后就會(huì)看到,。
一個(gè)新的EL操作符 在JSP頁面中一個(gè)非常普遍的需求就是:當(dāng)某個(gè)條件為真時(shí),要在網(wǎng)頁中包含一些文字,。在JSP1.2和JSTL1.1中,用具有代表性的<c:if>來實(shí)現(xiàn),,但是這樣做非常繁瑣,。JSP2.0增加了一個(gè)新的條件操作符用于EL,以更加優(yōu)雅的方式來處理這樣的情況。這個(gè)條件操作符存在于很多編程語言中(比如:Java,C,JavaScript),因此你可能以前就見過它,。它判斷一個(gè)布爾的條件,,當(dāng)條件為真或者假時(shí),分別取不同的結(jié)果,。
一個(gè)能清楚說明它如何工作的例子:
<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表達(dá)式和條件操作符來選擇是否包含 html 中的 “selected”屬性,只有符合條件的 “option” 才被添加 “selected” 屬性,。如果條件(param.artist==1)為真時(shí),,前面的“selected” 才被添加到網(wǎng)頁中;否則就添加后面的(在這里是空字符串 ‘’)到頁面中,。
EL函數(shù) 當(dāng)EL從JSTL規(guī)范中移到JSP規(guī)范中,,它使用了一個(gè)如何進(jìn)行函數(shù)調(diào)用的技巧。這個(gè)EL函數(shù)語法非常簡單:方法名,,緊接著在圓括號(hào)中有一組參數(shù):
<%@ taglib prefix="fn" uri="http://java./jsp/jstl/functions" %> ${fn:length(myCollection)} 這是一個(gè)屬于標(biāo)簽庫中的函數(shù),并且函數(shù)名字在頁面中所包含的前綴要指定taglib庫,。在這個(gè)例子中,我使用了前綴fn,這是JSTL function庫默認(rèn)的前綴,。
標(biāo)簽庫描述符(Tag Library Descriptor,TLD)將函數(shù)名稱映射到一個(gè)由JAVA實(shí)現(xiàn)的靜態(tài)方法中:
<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>,。它包含一個(gè)函數(shù)返回類型的聲明,靜態(tài)的方法的名字,,在圓括號(hào)中聲明該方法所有參數(shù)的類型(可以沒有參數(shù)或者有多個(gè),,參數(shù)間用逗號(hào)間隔開)。返回值類型和參數(shù)類型必須是java的原始類型(Object)或者是其他合法類型,。
這個(gè)靜態(tài)方法 length()在Jakarta Taglibs標(biāo)準(zhǔn)庫中用類似于下面的代碼實(shí)現(xiàn)的:
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")); }
就像你所看到的,,在那里沒有什么出奇的地方。它是一個(gè)常規(guī)的靜態(tài)方法,,這個(gè)函數(shù)中通過對運(yùn)行期中的參數(shù)類別的判斷,,找出參數(shù)的長度。
除了在這個(gè)方法中使用的length()方法,,JSTL1.1標(biāo)簽庫還包含了許多其它經(jīng)常使用的函數(shù):
函數(shù) 描述
fn:contains(string, substring) 如果參數(shù)string中包含參數(shù)substring,,返回true
fn:containsIgnoreCase(string, substring) 如果參數(shù)string中包含參數(shù)substring(忽略大小寫),返回true
fn:endsWith(string, suffix) 如果參數(shù) string 以參數(shù)suffix結(jié)尾,,返回true
fn:escapeXml(string) 將有特殊意義的XML (和HTML)轉(zhuǎn)換為對應(yīng)的XML character entity code,,并返回
fn:indexOf(string, substring) 返回參數(shù)substring在參數(shù)string中第一次出現(xiàn)的位置
fn:join(array, separator) 將一個(gè)給定的數(shù)組array用給定的間隔符separator串在一起,組成一個(gè)新的字符串并返回,。
fn:length(item) 返回參數(shù)item中包含元素的數(shù)量,。參數(shù)Item類型是數(shù)組、collection或者String,。如果是String類型,返回值是String中的字符數(shù),。
fn:replace(string, before, after) 返回一個(gè)String對象。用參數(shù)after字符串替換參數(shù)string中所有出現(xiàn)參數(shù)before字符串的地方,并返回替換后的結(jié)果
fn:split(string, separator) 返回一個(gè)數(shù)組,,以參數(shù)separator 為分割符分割參數(shù)string,,分割后的每一部分就是數(shù)組的一個(gè)元素
fn:startsWith(string, prefix) 如果參數(shù)string以參數(shù)prefix開頭,返回true
fn:substring(string, begin, end) 返回參數(shù)string部分字符串, 從參數(shù)begin開始到參數(shù)end位置,,包括end位置的字符
fn:substringAfter(string, substring) 返回參數(shù)substring在參數(shù)string中后面的那一部分字符串
fn:substringBefore(string, substring) 返回參數(shù)substring在參數(shù)string中前面的那一部分字符串
fn:toLowerCase(string) 將參數(shù)string所有的字符變?yōu)樾?,并將其返?br> fn:toUpperCase(string) 將參數(shù)string所有的字符變?yōu)榇髮懀⑵浞祷?br> fn:trim(string) 去除參數(shù)string 首尾的空格,,并將其返回
結(jié)束語: 在這篇文章中,,我從EL講到JSTL1.1規(guī)范、EL新特色和JSTL 1.1函數(shù)庫,。接下來的部分我將要告訴你:關(guān)于JSP error-page的改進(jìn)和增強(qiáng),; jsp:id 屬性帶來的益處;新的配置屬性描述符,;JSP2.0如何使JSP操作XML變得更加容易,;自定義標(biāo)簽庫的新特性。
JSP 2.0: The New Deal, Part 2 by Hans Bergsten, author of JavaServer Pages, 3rd Edition,,12/03/2003
這篇文章是講述加入到JavaServer Pages (JSP) 2.0 規(guī)范中的特性的系列文章的第二部分,。在前面的第一部分,我描述了新的EL表達(dá)式,,但是還有更多的內(nèi)容沒有涉及,。這一部分描述的是JavaServer Pages (JSP) 2.0 規(guī)范在錯(cuò)誤處理機(jī)制和新的部署描述符特性方面的增強(qiáng)。我假設(shè)你熟悉JSP 1.2,,而且至少聽說過JSP Standard Tag Library (JSTL),。
JSP Error Pages 如果你曾經(jīng)在JSP和servlet的錯(cuò)誤處理中使用過JSP error page,并且想要顯示或者記錄違例信息(違例導(dǎo)致JSP error page 被調(diào)用),,那么你就知道在JSP1.2中這并不是件輕松的事情,。原因是當(dāng)在servlets和JSP 頁面中聲明了一個(gè)errorPage,違例(exception)被作為一個(gè)request attribute傳遞,,它們要使用不同的屬性名稱,。只有被傳遞的違例通過JSP屬性名稱自動(dòng)地顯示在 JSP error page中(通過exception腳本變量和${pageContext.exception} EL 表達(dá)式)。
JSP 2.0通過將相同的屬性名稱轉(zhuǎn)換為servlet規(guī)范中的javax.servlet.error.exception來修正了這個(gè)問題,。更進(jìn)一步說,,一個(gè)新增的 命名為errorData的EL pageContext變量揭露了發(fā)生問題的其他信息。ErrorData屬性是javax.servlet.jsp.ErrorData的一個(gè)實(shí)例,。這個(gè)實(shí)例可以被 用來作為一個(gè)bean和以下的properties一同使用:
Property Java 類型 描述
requestURI String 發(fā)生請求失敗的 URI
servletName String 發(fā)生錯(cuò)誤的servlet或者JSP頁面的名稱
statusCode int 發(fā)生錯(cuò)誤的狀態(tài)碼
throwable Throwable 導(dǎo)致當(dāng)前error page被調(diào)用的違例
這里有個(gè)JSP error page的例子,。這個(gè)例子使用了上面提到的一些property:
<%@ page isErrorPage="true" contentType="text/html" %> <%@ taglib prefix="log" uri="http://jakarta./taglibs/log-1.0" %>
Sorry, but things didn‘t work out as planned. I‘ve logged as much as I know about the problem, so rest assured that my master will look into what‘s wrong as soon as he‘s sober.
<jsp:useBean id="now" class="java.util.Date" /> <log:fatal> ----- ${now} Request that failed: ${pageContext.errorData.requestURI} Status code: ${pageContext.errorData.statusCode} Exception: ${pageContext.errorData.throwable} ----- </log:fatal> 這個(gè)頁面使用Apache Jakarta Taglib項(xiàng)目中的 Log tag library來顯示一些確定的信息并記錄下了具體的細(xì)節(jié)。
你可以在web.xml文件中使用<error-page>來對servlet和JSP 頁面聲明這是一個(gè)error page:
... <error-page> <exception-type>java.lang.Throwable</exception-type> <location>/error.jsp</location> </error-page> <error-page> <exception-code>500</exception-code> <location>/error.jsp</location> </error-page> ... 如果你需要在某些特殊的JSP頁面中使用一個(gè)不同的error page,,可以用“@page”設(shè)置errorPage屬性來有選擇性地覆蓋web.xml中的聲明,。
JSP 語法錯(cuò)誤的報(bào)告
在JSP 1.2 和 JSP 2.0之間,,一個(gè)細(xì)微但是重要的區(qū)別是JSP 2.0需要JSP容器支持“jsp:id”特性,雖然在JSP 1.2中這僅僅是一個(gè)建議,。作為一個(gè)JSP開發(fā)者,這對你意味著什么呢,?對于JSTL和定制的標(biāo)簽庫的語法錯(cuò)誤,,它意味著你可以更好地獲得有用的錯(cuò)誤信息。
下面講述它是如何工作的,。當(dāng)JSP容器將JSP頁面轉(zhuǎn)換為可以執(zhí)行的servlet class,,容器著眼于這個(gè)頁面中聲明的所有標(biāo)簽庫。如果標(biāo)簽庫中的一個(gè)或者多個(gè)庫包含了一個(gè)標(biāo)簽庫驗(yàn)證器(Tag Library Validator,,TLV),,容器就會(huì)在接受這個(gè)JSP頁面前給予TLV一個(gè)檢驗(yàn)當(dāng)前頁面的機(jī)會(huì)。容器給予TLV一個(gè)當(dāng)前頁面的XML視圖用以分析,。XML視圖就像名稱暗示的那樣,,是當(dāng)前頁面的另外一種版本, 所有常規(guī)的JSP元素和template text已經(jīng)被轉(zhuǎn)換為一種結(jié)構(gòu)良好的XML文檔,。這個(gè)XML視圖對TLV來說是很容易解析的,,用于確定所有的custom actions被正確地使用(比如:custom actions被正確的嵌套,互斥的屬性是不能在同一個(gè)action element中一同使用的),。
這就是“jsp:id”的由來,。容器在頁面中給每一個(gè)custom action元素分配了一個(gè)ID,并且維護(hù)了一張?jiān)贗D和元素位置(文件,、行號(hào),、列號(hào))的映射表。添加的“jsp:id”屬性,,在XML視圖中ID值對應(yīng)于所有的custom action 元素,,這樣TLV就可以讀取它。如果TLV發(fā)現(xiàn)了一個(gè)語法錯(cuò)誤,,它就會(huì)在返回給容器的錯(cuò)誤信息中包含“jsp:id”屬性值來確定當(dāng)前無效的action element,。這時(shí)容器使用映射,并添加發(fā)生錯(cuò)誤的custom action element位置信息,,提示給你,。這使得開發(fā)人員找到并改正錯(cuò)誤變得輕而易舉。
所有的JSTL庫都要有TLV,。我強(qiáng)烈建議為你自己編寫的類庫開發(fā)TLV,而且任何第三方類庫的開發(fā)人員也應(yīng)該這樣做,。
如果你對XML視圖和TLV并不熟悉,我在2001年的介紹JSP 1.2的文章中有一個(gè)簡要的介紹("JSP 1.2: Great News for the JSP Community"),。
JSP 部署描述符 JSP 2.0在web.xml文件中使用了servlet規(guī)范中定義的部署描述符的文件格式,,就像早期的JSP規(guī)范中的那樣,。然而在JSP 2.0中有兩個(gè)重要的變化:1、現(xiàn)在web.xml文件的格式是通過XML Schema定義的,;2,、為了最大限度地減少servlet和JSP規(guī)范之間的耦合度,大多數(shù)的JSP配置細(xì)節(jié)已經(jīng)被移到一個(gè)新的XML element中,。
XML Schema是一種用來描述XML文檔語法規(guī)則的XML語言(可以定義XML element之間是如何嵌套的;一個(gè)element可以獲得什么樣的值; 值的唯一性的需求等等),。這是一個(gè)復(fù)雜的規(guī)范,但是幸運(yùn)的是你不用為了寫web.xml文件而需要明白XML Schema語法規(guī)則,。因?yàn)閟ervlet和JSP規(guī)范提供了易于理解的圖表( JavaServer Pages, 3rd Edition書中包含了許多簡單易懂的圖表),。如果你還是想要更進(jìn)一步了解XML Schema,請瀏覽W3C(http://www.)的網(wǎng)站,。
用XML Schema代替上一版本中的 Document Type Definition(DTD) 語言來聲明XML文檔結(jié)構(gòu)的主要優(yōu)點(diǎn)是:XML Schema具有更加富有表達(dá)能力,,因此在解析web.xml文件的過程中能發(fā)現(xiàn)更多的錯(cuò)誤,有希望解決在JSP容器之間更好地移植這個(gè)問題,。
另外一個(gè)優(yōu)點(diǎn)是(我確定你會(huì)感激于此的): 在web.xml文件中使用XML Schema可以使頂層的element按照任何順序排列變得輕而易舉,。舉例來說,在servlet和JSP 規(guī)范的上一個(gè)版本中,,如果將<error-page> element 放到<welcome-file-list> element 前面,,你將得到一個(gè)錯(cuò)誤提示。在新版本的規(guī)范中,,這樣做就不會(huì)有問題,。在頂層元素中的element的順序盡管還必須要按照嚴(yán)格的順序放置,但是在頂層元素以外至少你現(xiàn)在可以自由地支配了,。
除了<jsp-file>要嵌套在<servlet>中,,其它的element現(xiàn)在都被歸組到一個(gè)新的頂層element中,這個(gè)element命名為<jsp-config>,。在<jsp-config>中,,你可以使用<taglib> element,和在JSP 1.2中具有相同的語法和含義,,盡管它們并不需要實(shí)現(xiàn)了JSP 1.2 或者后續(xù)版本的容器,,因?yàn)樗鼈兛梢宰詣?dòng)地從已經(jīng)部署的JAR文件中獲得標(biāo)簽庫的定義。
新添加的<jsp-property-group>子元素更加有趣,。你可以用它來配置一組匹配某個(gè)指定的URL的JSP頁面,。比如:
... <jsp-config> <jsp-property-group> <url-pattern>*.jsp</url-pattern> <scripting-invalid>true</scripting-invalid> </jsp-property-group> </jsp-config> ... <url-pattern>元素確定這組JSP 頁面應(yīng)用哪一個(gè)配置。其他被嵌套的元素定義配置選項(xiàng),。這個(gè)例子中, <scripting-invalid>使應(yīng)用JSP scripting elements (Java code) 在所有的JSP 頁面中無效,。
總地來說同<scripting-invalid>一樣,你可以在<jsp-property-group>中使用以下的配置元素:
Element 描述
<el-ignored> 如果設(shè)置為true, 在匹配指定的URL模式的JSP 頁面中,,EL 表達(dá)式被當(dāng)作常規(guī)的文本而不是EL 表達(dá)式,。當(dāng)移植看起來有EL表達(dá)式文本的JSP 1.2頁面到JSP 2.0的時(shí)候,,這非常有用。在已經(jīng)轉(zhuǎn)換為JSP 2.0的頁面中,,你可以使用一個(gè)新增的“elIgnoredpage”屬性來選擇EL是否有效
<scripting-invalid> 如果設(shè)置為true, 在某個(gè)匹配的JSP頁面使用腳本
<page-encoding> 為匹配指定的URL模式的JSP頁面指定編碼,。這是一個(gè)可選的配置(在每一個(gè)JSP頁面中指定頁面的編碼),并且對于JSP頁面來說這是使用某些文件編碼(比如:EBCDIC)唯一的辦法
<include-coda> 為所有匹配指定的URL模式的JSP頁面的末尾自動(dòng)包含一個(gè)文件,,而指定相關(guān)的context路徑,。你可以在一個(gè)<jsp-property-group>中多次使用,或者通過多個(gè)<jsp-property-group>來實(shí)現(xiàn)
<include-prelude> 為所有匹配指定的URL模式的JSP頁面的開頭自動(dòng)包含一個(gè)文件,而指定相關(guān)的context路徑,。你可以在一個(gè)<jsp-property-group>中多次使用,或者通過多個(gè)<jsp-property-group>來實(shí)現(xiàn)
<is-xml> 如果設(shè)置為true, 所有匹配指定的URL模式的JSP頁面使用JSP XML語法(它們是JSP Document)
總結(jié) 在這部分,我講述了JSP 2.0增強(qiáng)的錯(cuò)誤處理機(jī)制和新的部署描述符的特性,。在這個(gè)系列文章中,,接下來的部分將涉及到JSP 2.0是如何使JSP操作XML變得輕松,還有與定制標(biāo)簽庫相關(guān)的新特性,。
JSP 2.0: The New Deal, Part 3 by Hans Bergsten, author of JavaServer Pages, 3rd Edition 04/21/2004
更具伸縮性的JSP Document格式規(guī)則 JSP 2.0 規(guī)范支持兩種類型的JSP頁面:一種是包含任何數(shù)據(jù)類型的常規(guī)JSP頁面,;另一種是具備良好結(jié)構(gòu)的XML文檔(具有XHTML和JSP元素)。為了做到這一點(diǎn),,在一個(gè)JSP Document中JSP的 “directive” 和 “腳本”必須用一種與常規(guī)的JSP頁面不同的語法來編寫:
常規(guī)的 JSP 頁面 JSP 文檔
<%@ page attribute list %> <jsp:directive.pageattribute list />
<%@ include file="path" %> <jsp:directive.include file="path" />
<%! declaration %> <jsp:declaration>declaration</jsp:declaration>
<%= expression %> <jsp:expression>expression</jsp:expression>
<% scriptlet %> <jsp:scriptlet>scriptlet</jsp:scriptlet>
在一個(gè)JSP Document中,,標(biāo)簽庫作為XML名稱空間被聲明。比如:一個(gè)JSP Document 包含來自標(biāo)準(zhǔn)JSTL核心庫中的XHTML模板文字和JSP action,, 它應(yīng)該有一個(gè)<html>根元素,,有以下的名稱空間的聲明:
<html xmlns="http://www./1999/xhtml" xmlns:jsp="http://java./JSP/Page" xmlns:c="http://java./jsp/jstl/core" xml:lang="en" lang="en"> xmlns屬性為XHTML名稱空間設(shè)置默認(rèn)的名稱空間,xmlns:jsp屬性把jsp前綴和定義為JSP標(biāo)準(zhǔn)action的元素關(guān)聯(lián)起來,,并且xmlns:c屬性將前綴“c”和JSTL核心庫定義的元素關(guān)聯(lián)起來,。
JSP Document已經(jīng)是JSP規(guī)范中的一部分了,但是起初是作為一個(gè)可選擇的特性,,并且后來還有許多的局限性,。JSP 2.0解決了大多數(shù)局限性的問題,使XML和JSP協(xié)同工作變得更加簡單,。
首先對于JSP 2.0來說,,一個(gè)JSP Document 必須要有一個(gè)<jsp:root>根元素用以告訴容器它是哪一種類型的JSP頁面。JSP 2.0通過這種新的方式來標(biāo)識(shí)一個(gè)JSP Document文件解決了這個(gè)限制,。如果以下條件中有一個(gè)為true,,這個(gè)文件就將被JSP 2.0容器作為一個(gè)JSP Document來處理:
請求的路徑與在web.xml中聲明的URL匹配, JSP property group 聲明有一個(gè) <is-xml>元素設(shè)置為 true,。關(guān)于 JSP property group 聲明在上一篇中有詳細(xì)的說明,。 請求路徑的擴(kuò)展名是.jspx,除非這個(gè)擴(kuò)展名匹配一個(gè)JSP property group聲明的URL pattern,,而JSP property group聲明<is-xml>元素為false,。 換句話說,,.jspx是默認(rèn)的JSP Document的擴(kuò)展名,但是它可以被一個(gè)property group的聲明置為無效,。 請求路徑擴(kuò)展名是.jsp或者匹配一個(gè)JSP property group聲明的URL pattern,,而且這個(gè)文件中的root element是<jsp:root>。 這些新的規(guī)則使采用一個(gè)常規(guī)的XHTML文件(用JSP element處理動(dòng)態(tài)內(nèi)容)的形式來編寫JSP Document成為可能,,比如:不需要將所有的內(nèi)容都放到<jsp:root> element中,。如果類似下面的例子那樣創(chuàng)建一個(gè)JSP property group,你甚至可以用擴(kuò)展名為.html的文件:
... <jsp-config> <jsp-property-group> <url-pattern>*.html</url-pattern> <is-xml>true</is-xml> </jsp-property-group> </jsp-config> ... 用新的方法來生成XML Element 如果你曾經(jīng)嘗試用JSP1.2來編寫JSP Document,,那么你很可能在給XML element的attribute動(dòng)態(tài)賦值時(shí)陷入了麻煩之中,。比如,你想要給一個(gè)XML element的 class attribute賦值為一個(gè)用來保存用戶風(fēng)格樣式參數(shù)的bean property,。你首先可能試圖像下面這樣做:
<table class="%= user.getTableClass() %"> 這樣類型的Java 表達(dá)式,,在一個(gè)JSP Document中能被用來作為一個(gè) JSP action element的屬性值。但是在template text中,,JSP不會(huì)識(shí)別這樣的語法,,因此在這里是不會(huì)工作的。
用一個(gè)JSP action element來設(shè)置attribute的值,,同樣不可以:
<table class="<c:out value="${user.tableClass}" />">
這是因?yàn)樵谝粋€(gè)element attribute中一個(gè)格式完整的XML 文檔不可以有一個(gè)“<”符號(hào),。
在JSP 1.2中,唯一動(dòng)態(tài)設(shè)置一個(gè)element attribute 值的方法是使用令人生厭的CDATA 片斷,,將開始和結(jié)束的element當(dāng)作原始文本(被動(dòng)態(tài)生成的值包裝)來處理:
<jsp:text><!CDATA[<table class="]]></jsp:text> <c:out value="${user.tableClass}" /> <jsp:text><!CDATA[">]]></jsp:text> JSP 2.0對于這種情況提供了兩種簡單的可選方案:1,、在template text中使用一個(gè)EL表達(dá)式;2,、使用一組新的標(biāo)準(zhǔn)的action element生成element,。用EL表達(dá)式的例子如下:
<table class="${user.tableClass}"> 一個(gè)JSP 2.0的容器對在template text中遇到的EL表達(dá)式求值,與在action 屬性中一樣,。因此在大多數(shù)的情況下,,這個(gè)解決方案是合適的。
如果你不能表示出你想要分配的值,,你可以用三個(gè)新的標(biāo)準(zhǔn)action動(dòng)態(tài)構(gòu)造整個(gè)XML element,,用JSP代碼生成attribute的值。
<jsp:element name="table"> <jsp:attribute name="class"> <c:out value="${user.tableClass}" /> </jsp:attribute> <jsp:body> ... </jsp:body> </jsp:element> 這個(gè) <jsp:element> action 創(chuàng)建一個(gè)XML element,,它的屬性是由嵌套的<jsp:attribute> action 所創(chuàng)建的,。這個(gè)屬性的值被設(shè)置為<jsp:attribute>的運(yùn)算結(jié)果,因此你可以使用自定義的action來產(chǎn)生它,,比如:在這個(gè)例子中的<c:out> action,。同樣的element被設(shè)置為一個(gè)嵌套的<jsp:body>的運(yùn)算結(jié)果。
一個(gè)用來聲明XML的新的標(biāo)準(zhǔn)Action 一個(gè)XML document應(yīng)該在文檔的起始位置有一個(gè)XML的聲明,,可能跟隨著一個(gè)DOCTYPE的聲明,。在JSP 2.0中,,你可以用新增的<jsp:output>標(biāo)準(zhǔn) action來生成這兩個(gè)聲明。
除非這個(gè)JSP Document有一個(gè)<jsp:root> element,,就像它的root element(或者一個(gè)標(biāo)簽文件,,標(biāo)簽文件我將在下一篇文章中講述),JSP容器默認(rèn)創(chuàng)建一個(gè)XML聲明,,就像這樣:
<? xml version="1.0" encoding="encodingValue" ?> encoding屬性的值是字符編碼,,是由JSP頁面的contentType屬性決定的。如果你沒有指定一個(gè)字符集,,默認(rèn)的就是UTF-8編碼,。如果你不希望生成一個(gè)XML的聲明(因?yàn)檫@個(gè)JSP Document可能被包含在另外一個(gè)JSP頁面中),你需要通過包含一個(gè)<jsp:output> action element來告訴JSP容器,就像這個(gè)JSP Document中那樣:
<jsp:output omit-xml-declaration="true" /> 用屬性值true或者 yes來禁止生成這個(gè)聲明,。用false或者 no來允許生成這個(gè)聲明,。
一個(gè)DOCTYPE聲明告訴一個(gè)XML parser(例如一個(gè)瀏覽器使用的parser)這個(gè)XML文檔遵循了哪一個(gè)DTD(Document Type Declaration)。Parser可以用此信息來確認(rèn)這個(gè)XML文檔包含的僅是這個(gè)DTD聲明的XML element,。你不能在這個(gè)生成的JSP Documen中放置DOCTYPE聲明(因?yàn)榇藭r(shí)你正在表明這個(gè) JSP Document和DTD一起編譯),而是使用<jsp:output> action來告訴JSP容器為生成的結(jié)果添加一個(gè)聲明:
<jsp:output doctype-root-element="html" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" doctype-system="http://www./TR/xhtml1/DTD/xhtml1-transitional.dtd"/>
<jsp:directive.page contentType="text/html" /> 就像這個(gè)例子一樣,,<jsp:output> action為XHTML給輸出的結(jié)果添加了一個(gè)DOCTYPE聲明:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www./TR/xhtml1/DTD/xhtml1-transitional.dtd"> 在這個(gè)例子中,,我也包含了一個(gè)有contentType屬性的<jsp:directive.page>聲明,用來給輸出的內(nèi)容設(shè)置MIME類型為text/html,,用來告訴瀏覽器如何對輸出的內(nèi)容進(jìn)行處理,。記住,因?yàn)閄HTML特有的MIME類型實(shí)際是application/xhtml+xml,但是一些比較新的瀏覽器(特別是Internet Explorer 6)并不認(rèn)識(shí)它,,對于知道如何處理XHTML 1.0的大多數(shù)瀏覽器來說text/html才是一種允許接受的MIME類型,。
結(jié)論 JSP2.0用XML document方式編寫JSP頁面更加容易,就像你已經(jīng)在這部分看到的那樣,。最后一篇文章將要講述和自定義標(biāo)簽庫相關(guān)的新特性:新的標(biāo)簽文件格式和新的簡單的標(biāo)簽API,。 JSP 2.0: The New Deal, Part 4
byHans Bergsten, author of JavaServer Pages, 3rd Edition05/12/2004
在“JSP 2.0: 新特性”的最后一部分中,我們將要看兩個(gè)新特性,,它們使開發(fā)自定義標(biāo)簽庫變得更加容易,,它們是:標(biāo)簽文件和簡化的tag-handler Java API。
以JSP Tag文件形式開發(fā)自定義Action
JSP試圖讓對Java沒有經(jīng)驗(yàn)的人寫動(dòng)態(tài)頁面成為可能,。在頁面間,,重用一部分動(dòng)態(tài)的、復(fù)雜的內(nèi)容到目前為止仍然是令人痛苦的事情,。舉例來說,,你想要在一些頁面中放置一個(gè)投票程序,包括問題和答案,。在 JSP 2.0之前,,你有三個(gè)選擇:1,、你可以拷貝并粘貼投票的程序到你希望添加的每一個(gè)頁面中;2,、一個(gè)稍好的選擇是編寫一個(gè)獨(dú)立的JSP頁面來生成投票的表單,,并且在其他頁面中用<jsp:include>或者 <c:import> action來包含這個(gè)獨(dú)立的頁面。但是你僅僅可以通過它來輸入String類型的參數(shù),,不能是一個(gè)保存答案的bean或者一個(gè)Map,。3、用一個(gè)Java tag handler類實(shí)現(xiàn)一個(gè)自定義action,, 但是這需要建立在你對Java的了解基礎(chǔ)上,。
JSP 2.0增加了第四種選擇:也就是用標(biāo)簽文件的形式開發(fā)自定義action。一個(gè)標(biāo)簽文件是一個(gè)純文本文件,,你對所有的動(dòng)態(tài)生成的部分使用JSP element,,就像在常規(guī)的JSP頁面中那樣。和一個(gè)Java 標(biāo)簽handler有同樣目的,,即:為自定義的action 提供邏輯運(yùn)算,。標(biāo)簽文件和JSP 頁面之間主要的區(qū)別是一個(gè)標(biāo)簽文件有一個(gè)以.tag為擴(kuò)展名的標(biāo)簽文件,用這個(gè)擴(kuò)展名來區(qū)別jsp頁面,,并且你可以使用僅在標(biāo)簽文件中有效的一些新的標(biāo)簽來定義輸入和輸出,。
更進(jìn)一步來看,這里有一個(gè)標(biāo)簽文件“poll.tag”,,它生成一個(gè)投票的表單:
<%@ tag body-content="empty" %> <%@ attribute name="question" required="true" %> <%@ attribute name="answers" required="true" type="java.lang.Object" %> <%@ attribute name="votesMapName" required="true" %> <%@ attribute name="answersMapName" required="true" %> <%@ taglib prefix="c" uri="http://java./jsp/jstl/core" %>
Question: ${question}<br> <form action="result.jsp" target="result"> <input type="hidden" name="question" value="${question}"> <input type="hidden" name="votesMapName" value="${votesMapName}"> <input type="hidden" name="answersMapName" value="${answersMapName}"> <c:forEach items="${answers}" var="a"> <input type="radio" name="vote" value="${a.key}">${a.value}<br> </c:forEach> <input type="submit" value="Vote"> </form> 在文件的頂部,,有一個(gè)標(biāo)簽指示符“tag”。這個(gè)標(biāo)簽指示符和你在JSP頁面中使用的頁面指示符“page”相似,,它聲明該文件的性質(zhì),。這里,我使用了body-content屬性來聲明:在一個(gè)JSP頁面中,,一個(gè)表現(xiàn)這個(gè)標(biāo)簽文件的action element必須是empty,,也就是這個(gè)action element不能有body。其他的值你可以使用“scriptless”(body可以包含除了腳本元素以外的任何期望的內(nèi)容),,或者“tagdependent”(容器傳遞主體內(nèi)容給標(biāo)簽處理器(tag handler)而不做任何運(yùn)算),。如果你曾經(jīng)以Java class這種形式開發(fā)過自定義action,你可能就會(huì)從用來聲明Java 標(biāo)簽處理器的TLD (Tag Library Descriptor)中認(rèn)出“body-content”和“valid”。因?yàn)橐粋€(gè)標(biāo)簽文件并不需要在一個(gè)TLD文件中被聲明,,標(biāo)簽指示符和其他特殊的標(biāo)簽文件指示符被用來提供相同類型的信息,,TLD將這個(gè)信息提供給JSP容器。
在這個(gè)例子中跟隨著這個(gè)標(biāo)簽指示符的是attribute指示符,,用于同樣的函數(shù),,和TLD 的element一樣有著相同的名字:它聲明有效的自定義action element 屬性。這個(gè)自定義的用于投票的標(biāo)簽文件接受四個(gè)屬性:
question: 投票的問題 answers:一個(gè)具有application-scope作用范圍的Map,用于保存投票的答案,用數(shù)字做主鍵(key) votesMapName:一個(gè)具有application-scope作用范圍的變量名字,,用于一個(gè)Map 保存每個(gè)答案的得票數(shù),,用答案數(shù)量做主鍵 answersMapName:一個(gè)具有application-scope作用范圍的變量名字,用于保存答案的Map 每一個(gè)action element屬性都有一個(gè)attribute指示符對應(yīng),,它的名字用“name”屬性來聲明,。在這個(gè)例子中,所有的action element屬性都是需要的,,就像對每一個(gè)attribute指示符都要用required屬性來聲明一樣,。“answers”屬性值必須是一個(gè)“Map”,,它被” type”屬性來聲明,。所有其它的屬性必須是String類型的,這是默認(rèn)的類型,,因此我沒有特別為它們的屬性指出類型,。
一個(gè)taglib聲明:在這個(gè)文件中使用了JSTL核心庫,在一個(gè)用來輸出question和每一個(gè)answer都有一個(gè)radio單選按鈕的表單的EL表達(dá)式中,,文件的主體使用了屬性值(以page-scope變量形式,,對標(biāo)簽文件有效)。answer 和 vote Map變量名字和question在表單中以隱藏域的形式在表單中出現(xiàn),,因此它們通過頁面?zhèn)鬟f被處理,,它們可以像下面這樣應(yīng)用:
<%@ page contentType="text/html" %> <%@ taglib prefix="c" uri="http://java./jsp/jstl/core" %> <html> <head> <title>Poll Results</title> </head> <body bgcolor="white"> <c:set target="${applicationScope[param.votesMapName]}" property="${param.vote}" value="${applicationScope[param.votesMapName][param.vote] + 1}" /> <p> Question: ${param.question}<br> <c:forEach items="${applicationScope[param.answersMapName]}" var="a"> ${a.key}) ${a.value}: ${applicationScope[param.votesMapName][a.key]}<br> </c:forEach> </p> </body> </html> 這是一個(gè)常規(guī)的JSP頁面,使用了JSTL action,。在保存投票結(jié)果的Map中,它對符合鍵值條件的被選中的投票答案加一,。通過votesMapName參數(shù)提供名字,,在application作用范圍內(nèi)有效。接下來,,在頁面上輸出question并且通過Map迭代輸出answer,,通過answersMapName參數(shù)提供名字在application作用范圍內(nèi)有效, 向頁面輸出每一個(gè)answer和當(dāng)前answer的得票數(shù)。
比如何處理poll vote更令人感興趣的是如何在JSP頁面中使用由poll tag標(biāo)簽文件實(shí)現(xiàn)的自定義action,,這里有一個(gè)例子:
<%@ page contentType="text/html" %> <%@ taglib prefix="c" uri="http://java./jsp/jstl/core" %> <%@ taglib prefix="my" tagdir="/WEB-INF/tags/mytags" %>
<html> <head> <title>My Page</title> </head> <body bgcolor="white"> <jsp:useBean id="myAnswers" scope="application" class="java.util.TreeMap"> <c:set target="${myAnswers}" property="1" value="Yes" /> <c:set target="${myAnswers}" property="2" value="No" /> <c:set target="${myAnswers}" property="3" value="Maybe" /> </jsp:useBean> <jsp:useBean id="myVotes" scope="application" class="java.util.HashMap" /> ... <p> <my:poll question="Will you start using tag files?" answers="${myAnswers}" answersMapName="myAnswers" votesMapName="myVotes" /> </p> ... </body> </html> 首先要注意的是“taglib”標(biāo)簽指示符,,它聲明使用的這個(gè)標(biāo)簽文件所在的標(biāo)簽庫。要使用一個(gè)沒有創(chuàng)建TLD的標(biāo)簽文件,,你必須將這個(gè)標(biāo)簽文件保存在WEB-INF/tags目錄下,。例子中的poll.tag文件保存在WEB-INF/tags/mytags目錄下,并且我讓這個(gè)目錄名字和“taglib”的“tagdir”屬性的值相同,。這樣就告訴容器在這個(gè)目錄下所找到的所有標(biāo)簽文件都屬于同一個(gè)標(biāo)簽庫,,上面的例子中,在這個(gè)庫中的action element被“taglib” 標(biāo)簽指示符的“prefix” 屬性所指定的前綴所標(biāo)識(shí)。另一種可選擇的方式是:你可以將這些屬于某個(gè)TLD的標(biāo)簽文件打包到一個(gè)jar文件中,,用同樣的“taglib” 標(biāo)簽指示符聲明你使用了一個(gè)自定義標(biāo)簽庫,,并且用一個(gè)“uri”屬性來替換“tagdir”屬性。
在這個(gè)例子中,,用于answer的“Map”對象和vote的計(jì)數(shù)由<jsp:useBean> action創(chuàng)建的,,并且由JSTL的<c:set> action來操作,你當(dāng)然可以用任何喜歡的方式來創(chuàng)建它們(比如:在一個(gè)context listener中,,或者在Apache Struts application中用一個(gè)“plugin” class來創(chuàng)建),。無論它是如何被創(chuàng)建的,這個(gè)標(biāo)簽文件都是通過一個(gè)常規(guī)的JSP自定義action element被調(diào)用的,。我使用一個(gè)EL表達(dá)式來對這個(gè)answers “Map”求值(“answers”的屬性值),。標(biāo)簽文件默認(rèn)是接受EL表達(dá)式的,你可以聲明:一個(gè)靜態(tài)值必須用“attribute”指示符的“rtexprvalue”屬性來指定,。
當(dāng)你請求這個(gè)頁面的時(shí)候,,JSP容器就像通常一樣對這個(gè)頁面進(jìn)行處理,通過element的“prefix” 和 “taglib“指示符的幫助來查找并定位 “<my:poll>”自定義標(biāo)簽的實(shí)現(xiàn),。容器可以按照它想要的任何方式來處理這個(gè)標(biāo)簽文件,;Tomcat 5 容器把標(biāo)簽文件轉(zhuǎn)換為一個(gè)Java標(biāo)簽處理器的類(tag handler class),編譯并執(zhí)行它。
處理自定義Action 就像用java語言編寫的tag handler類一樣,,一個(gè)標(biāo)簽文件能要求容器處理自定義action element,,更進(jìn)一步地處理運(yùn)算結(jié)果。
下面,,在action element body中我們添加一個(gè)上一篇文章中poll question的小例子,,就像下面這樣:
<%@ page contentType="text/html" %> <%@ taglib prefix="c" uri="http://java./jsp/jstl/core" %> <%@ taglib prefix="my" tagdir="/WEB-INF/tags/mytags" %> ... <html> ... <body bgcolor="white"> ... <p> <my:poll question="Will you start using tag files?" answers="${myAnswers}" answersMapName="myAnswers" votesMapName="myVotes" > JSP 2.0 introduces a new way to develop custom action tag handlers, called <i>tag files</i> </my:poll> </p> ... </body> </html> 這個(gè)例子僅僅包含文本,但是它也可以包含action element和EL 表達(dá)式,。為了處理action element body并且講結(jié)果添加到response中,,我們需要象下面這樣修改poll tag 文件:
<%@ tag body-content="scriptless" %> <%@ attribute name="question" required="true" %> <%@ attribute name="answers" required="true" type="java.lang.Object" %> <%@ attribute name="votesMapName" required="true" %> <%@ attribute name="answersMapName" required="true" %> <%@ taglib prefix="c" uri="http://java./jsp/jstl/core" %>
<p> <jsp:doBody/> </p> Question: ${question}<br> <form action="result.jsp" target="result"> <input type="hidden" name="question" value="${question}"> <input type="hidden" name="votesMapName" value="${votesMapName}"> <input type="hidden" name="answersMapName" value="${answersMapName}"> <c:forEach items="${answers}" var="a"> <input type="radio" name="vote" value="${a.key}">${a.value}<br> </c:forEach> <input type="submit" value="Vote"> </form> 首先,我們改變“tag”指示符的“body-content”屬性值為“scriptless”,。就像我前面說過的那樣,,這意味著在這個(gè)文件的主體內(nèi)容中可以包含除了scripting element以外的任何內(nèi)容。接下來,,我們添加一個(gè)“<jsp:doBody>” action,,它告訴容器去處理body,并且將處理結(jié)果添加到response中。你可以有選擇性的使用“var” 屬性,,捕獲處理的結(jié)果,,并在下一步進(jìn)行處理。
另外對于我已經(jīng)講述的新特性來說,,一個(gè)標(biāo)簽文件可以通過變量,,未聲明的屬性將信息返回給調(diào)用文件,,并且可以有 “fragment” 屬性(也就是說這些屬性可以擁有action element和EL表達(dá)式,標(biāo)簽文件處理EL表達(dá)式采用的方式與處理action element類似),。在我這本書第11章中,,你可以看到關(guān)于這些特性的全部內(nèi)容,你可以在http://www./catalog/jserverpages3/chapter/下載程序范例,。
簡單的Java Tag-Handler API 采用標(biāo)簽文件的方式編寫自定義action tag handler,,這是一個(gè)重大的新特性。尤其對自定義action來說,,它往往要生成很多的HTML,。但是在處理某些事情時(shí),僅僅用JSP action 和 EL 表達(dá)式是非常難以做到的,,因此仍然還是需要Java tag-handler API ,。首先,對于JSP 2.0來說,,用Java編寫一個(gè)tag handler是非常復(fù)雜的,。這是由于在容器和tag handler之間的交互需要處理action element body,而為了在action element body中支持Java scripting element就變得復(fù)雜起來,!畢竟,,如果在body中僅僅是包含template text、EL表達(dá)式,、action element的話,,一種相對簡單的API就可以被設(shè)計(jì)出來。而這就恰恰是JSP 2.0所做的,,并且它被恰當(dāng)?shù)孛麨閟imple tag handler API,。
這里有一個(gè)前面的范例poll的自定義action的例子,在前面我們是用一個(gè)標(biāo)簽文件來實(shí)現(xiàn)的:
package com.mycompany.mylib;
import java.io.IOException; import java.util.Iterator; import java.util.Map; import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspWriter; import javax.servlet.jsp.tagext.JspFragment; import javax.servlet.jsp.tagext.SimpleTagSupport;
public class PollTag extends SimpleTagSupport { private String question; private Map answers; private String votesMapName; private String answersMapName;
public void setQuestion(String question) { this.question = question; }
public void setAnswers(Map answers) { this.answers = answers; }
public void setVotesMapName(String votesMapName) { this.votesMapName = votesMapName; }
public void setAnswersMapName(String answersMapName) { this.answersMapName = answersMapName; }
public void doTag() throws JspException, IOException { JspWriter out = getJspContext().getOut(); JspFragment body = getJspBody(); if (body != null) { out.println("<p>"); body.invoke(null); out.println("</p>"); } out.print("Question:"); out.print(question); out.println("<br>"); out.println("<form action=\"result.jsp\" target=\"result\">"); out.print("<input type=\"hidden\" name=\"question\" value=\""); out.print(question); out.println("\">"); out.print("<input type=\"hidden\" name=\"votesMapName\" value=\""); out.print(votesMapName); out.println("\">"); out.print("<input type=\"hidden\" name=\"answersMapName\" value=\""); out.print(answersMapName); out.println("\">"); Iterator i = answers.keySet().iterator(); while (i.hasNext()) { String key = (String) i.next(); String value = (String) answers.get(key); out.print("<input type=\"radio\" name=\"vote\" value=\""); out.print(key); out.print("\">"); out.print(value); out.println("<br>"); } out.println("<input type=\"submit\" value=\"Vote\">"); out.println("</form>"); } } 一個(gè)singple tag必須實(shí)現(xiàn)新增的javax.servlet.jsp.tagext.SimpleTag接口,。這個(gè)例子中的tag handler繼承javax.servlet.jsp.tagext.SimpleTagSupport類,,父類提供了所有方法默認(rèn)的實(shí)現(xiàn)。就像經(jīng)典的tag handler類一樣,,你需要給每一個(gè)自定義的action屬性創(chuàng)建setter方法,但是這里僅僅有一個(gè)處理方法,,那就是要實(shí)現(xiàn)doTag()方法,。
在tag handler這個(gè)例子中,方法doTag()試圖通過調(diào)用getJspBody()方法(從SimpleTagSupport類繼承而來)而獲得一個(gè)可以執(zhí)行的action element body(一個(gè)JspFragment實(shí)例)的請求,。如果找到這個(gè)body, tag handler就用一個(gè)null 值來調(diào)用它,,就像對invoke()方法的爭論一樣,這意味著處理的結(jié)果被添加到response輸出中,。至于<jsp:doBody> action,,通過一個(gè)Writer實(shí)例來代替invoke()方法,你能捕獲到處理的結(jié)果。此時(shí)doTag()方法輸出帶有radio單選按鈕的HTML表單,,就像標(biāo)簽文件的實(shí)現(xiàn)一樣,。
因?yàn)槿萜髦荒苷{(diào)用一個(gè)方法來讓tag handler處理自己該做的事情,這代替了經(jīng)典的tag handler的三個(gè)方法(doStartTag(), doAfterBody(), and doEndTag(),,每一個(gè)方法的返回值告訴容器下一步該做什么),,實(shí)現(xiàn)一個(gè)SimpleTag的tag handler比經(jīng)典的tag-handler API更容易。另外,,simple tag-handler的實(shí)例永遠(yuǎn)不能被再次使用,,因此你不需要擔(dān)心重置后的狀態(tài),而它的狀態(tài)將會(huì)導(dǎo)致很多的問題,。在我前面的文章中描述過這個(gè)問題,,具體見"JSP 1.2: Great News for the JSP Community, Part 2"。
在一個(gè)TLD中聲明一個(gè)simple tag handler,,所用的方式與你聲明一個(gè)經(jīng)典的tag handler一樣,。但是除了JSP,<body-content> TLD element必須有一個(gè)值,。這是因?yàn)閟imple tag handler不允許scripting element出現(xiàn)在自定義action的element body中,。除此之外,使用一個(gè)實(shí)現(xiàn)了simple tag handler的自定義action與使用一個(gè)實(shí)現(xiàn)了經(jīng)典tag handler的action沒有任何區(qū)別,。
simple tag handler 和 classic tag handler都可以通過實(shí)現(xiàn)一個(gè)新的接口(javax.faces.jsp.tagext.DynamicAttributes)來支持未聲明的attribute,。兩種類型的attribute 都可以是JspFragment,包含其他的action或者EL表達(dá)式,,tag handler可以用它進(jìn)行任意多次的處理,。在我的《JavaServer Pages, 3rd Edition》這本書中,你可以看到更多關(guān)于這些特性的內(nèi)容,。
小節(jié) 在以上系列文章中,,我已經(jīng)向你展示了JSP 2.0中所有的新特性:EL表達(dá)式,更好的錯(cuò)誤處理機(jī)制,,新的配置選項(xiàng),,改進(jìn)的XML頁面格式,用來開發(fā)tag handler的兩個(gè)新途徑,??傊@些改進(jìn)之處使開發(fā)JSP頁面變得易于理解和維護(hù),。
如果你想要嘗試JSP2.0的新特性,,我建議你使用Apache Tomcat 5。它是最早實(shí)現(xiàn)了JSP新規(guī)范的容器之一,。在公布最終版本的JSP 2.0規(guī)范之前,,一個(gè)被標(biāo)記為“stable”版本的Tomcat是不能被發(fā)布的,。但是最新的beta版已經(jīng)被證實(shí)是非常穩(wěn)定的,不要理會(huì)beta版的標(biāo)記,。Tomcat 5在the Jakarta Project site可以下載,。
Hans Bergsten is the founder of Gefion Software and author of O‘Reilly‘s JavaServer Pages, 3rd Edition.
|
|