用於調用Javaean組件中的操作和執行請求指派的標準JSP標籤簡化了JSP頁面的開發和維護。JSP技術還提供了在
自訂標籤中封裝其他動態功能的機制,這種自定標籤是JSP語言的擴充。自訂標籤通常是以
標籤庫的形式出現的,它定義了一組相關的自訂標籤,並包含實現這些標籤的對象。
可 以由自訂標籤執行的任務包括對隱式對象的操作、處理表單、訪問資料庫和其他企業級服務,如電子郵件和目錄、以及執行流程式控制制。JSP標籤庫是由精通 Java程式設計語言和對訪問資料庫和其他服務非常熟悉的開發人員建立的,使用這些標籤,Web應用程式開發人員就可以把注意力放到內容的呈現上,而不用費心 考慮如何訪問企業級服務。就像鼓勵將庫開發人員和庫使用人員的工作分開一樣,自訂標籤通過封裝反覆執行的任務使它們可以在多個應用程式中重複使用,從而 提高了生產率。
JSP技術社區給予標籤庫非常高的重視。有關標籤庫的資訊和一些免費的庫的地址,參見
http://java.sun.com/products/jsp/taglibraries.html
什麼是自訂標籤?
自訂標籤是使用者定義的JSP語言元素。當包含自訂標籤的JSP頁面轉換為servlet時,這個標籤就轉換為一個名為tag handler的對象上的操作。之後當JSP頁面的servlet執行時,Web容器就調用這些操作。
自訂標籤有豐富的功能。它們可以
· 通過從調用頁面傳遞的屬性進行定製。
· 訪問JSP頁面可以使用的所有對象。
· 修改由調用頁面產生的響應。
· 彼此通訊。可以建立並初始化JavaBean組件、在一個標籤中建立引用該bean的變數、再在另一個標籤中使用這個bean。
· 彼此嵌套,可以在JSP頁面中實現複雜的互動互動。
JSP
頁面樣本
本章描述使用和定義標籤所涉及的任務。本章用改寫了的、在JSP頁面樣本中討論的JSP版本的Duke’s Bookstore應用程式的部分示範這些任務,所做的改寫利用了兩個標籤庫的優點:Struts和tutorial-template。本章的第三節樣本詳細描述了兩個標籤:Strutst中的iterate和tutorial-template標籤庫中的一組標籤。
Struts標籤庫提供了構建實現模型-視圖-控制設計模式的國際化Web應用程式的架構。Struts包括完整的一組自訂工具標籤,用於處理:
· HTML 表單
· 模板
· JavaBeans組件
· 邏輯處理
Duke's Bookstore應用程式使用Struts bean和logic子庫中的標籤。
Tutorial -template標籤庫定義了一組用於建立應用程式模板的標籤。模板是帶有預留位置的JSP頁面,這些預留位置需要在每一螢幕中改變。每一個預留位置稱為模板 的參數。例如,一個簡單的模板可能包括在產生的螢幕上方的title參數,和一個JSP頁面作為螢幕的定製內容的body參數。模板是用一組嵌入的標籤創 建的——definition、screen和parameter——它們用於構建Duke's Bookstore的螢幕定義表,並用insert標籤將參數從表中插入螢幕。
圖16-1顯示了通過Duke's Bookstore Web組件的請求流程:
· template.jsp, template.jsp確定每一螢幕的結構。它使用insert標籤用子組件組成螢幕。
· screendefinitions.jsp,它定義了每一螢幕使用的子組件。所有螢幕都有相同的橫幅,但是標題和本文不同(由表15-1中的JSP頁面列所指定)。
· Dispatcher,這是一個servlet,它處理請求並轉寄給template.jsp。
圖16-1 通過Duke's Bookstore組件的請求流程
Duke's Bookstore應用程式的原始碼位於在解壓縮教程包(見運行樣本)時產生的docs/tutorial/examples/web/bookstore3目錄中。要編譯、部署並運行這個例子,你需要:
1. 從以下地址下載Struts version 1.0.2
http://jakarta.apache.org/builds/jakarta-struts/release/v1.0.2/
2. 解壓縮Struts並將struts-bean.tld、struts-logic.tld和struts.jar從jakarta-struts-1.0/lib拷貝到<JWSDP_HOME>/docs/tutorial/examples/web/bookstore3。
3. 在終端視窗,進入<JWSDP_HOME>/docs/tutorial/examples/bookstore3.
4. 運行ant build。Build目標會進行所有必要的編譯並將檔案拷貝到<JWSDP_HOME>/docs/tutorial/examples/web/bookstore3/build目標。
5. 確保已經啟動了Tomcat。
6. 運行ant install。Install目標式通知Tomcat 已經有了內容。
7. 如果還沒有做的話,就啟動PointBas資料庫伺服器並加入資料(見從Web應用程式中訪問資料庫)。
8. 開啟書店URL http://localhost:8080/bookstore3/enter。
有關診斷常見問題的協助見常見問題及其解決方案和故障排除。
使用標籤
本節描述JSP頁面如何使用標籤,並介紹不同類型的標籤。
要使用標籤,頁面編寫者必須做以下兩件事:
· 聲明包含標籤的標籤庫
· 讓標籤庫實現對於Web應用程式可用
聲明標籤庫
通過在使用任何自訂標籤之前,將taglib指令加入頁面中聲明JSP頁面將使用在標籤庫中定義的標籤:
<%@ taglib uri="/WEB-INF/tutorial-template.tld" prefix="tt" %>
uri屬性工作表示唯一標識標籤庫描述符(TLD)的URI,在標籤庫描述符中描述了uri。這個URI可以是直接或者非直接的。prefix屬性定義了區分指定標籤庫所定義的標籤與其他標籤庫提供的標籤的首碼。
標籤庫描述符檔案名稱必須有副檔名.tld。TLD檔案儲存在WAR的WEB-INF目錄中,或者在WEB-INF的子目錄中。可以直接或者間接引用TLD。
下面taglib指令直接引用一個TLD檔案名稱:
<%@ taglib uri="/WEB-INF/tutorial-template.tld" prefix="tt" %>
這個taglib指令使用一個短的邏輯名間接引用TLD:
<%@ taglib uri="/tutorial-template" prefix="tt" %>
在Web應用程式部署描述符中將邏輯名映射到一個絕對位置。要將邏輯名/tutorial-template映射為絕對位置/WEB-INF/tutorial-template.tld,在web.xml中添加元素taglib:
<taglib>
<taglib-uri>/tutorial-template</taglib-uri>
<taglib-location>
/WEB-INF/tutorial-template.tld
</taglib-location>
</taglib>
讓標籤庫實現可用
可 以以兩種方式讓標籤庫實現對Web應用程式可用。實現了標籤handler的類可以以非打包的形式儲存在Web應用程式的WEB-INF/classes 子目錄中。另一種方法是,如果以JAR的形式發布庫,就將它儲存在Web應用程式的WEB-INF/lib目錄中。在多個應用程式中共用的標籤庫儲存在 Java WSDP的<JWSDP_HOME>/common/lib目錄中。
標籤類型
JSP自訂標籤是用XML文法編寫的。它們有一個開始標籤和結束標籤,可能還有本文:
<tt:tag>
body
</tt:tag>
不帶本文的自訂標籤如下表示:
<tt:tag />
簡單標籤
一個簡單標籤沒有本文,也沒有屬性:
<tt:simple />
帶屬性的標籤
自訂標籤可以帶有屬性。屬性列在開始標籤中,文法為attr="value"。像用參數定製方法的行為一樣,屬性值用於定製自訂標籤的行為。在標籤庫描述符中指定標籤屬性的類型(見帶屬性的標籤)。
可以用一個常量或者運行時運算式設定屬性值。常量和運行時運算式與屬性類型之間的轉換過程遵循在設定JavaBean組件屬性中描述的JavaBean組件屬性規則。
Struts logic:present標籤的屬性決定是否對標籤的本文進行判斷。在下面的例子中,一個屬性指定需要一個名為的參數Clear:
<logic:present parameter="Clear">
Duke's Bookstore應用程式頁面catalog.jsp使用了運行時運算式設定屬性的值,它決定Struts logic:iterate標籤要枚舉哪幾本書。
<logic:iterate collection="<%=bookDB.getBooks()%>"
id="book" type="database.BookDetails">
帶本文的標籤
自訂標籤可以包含自訂和核心標籤、指令碼元素、HTML文本和開始與結束標籤之間的、依賴於標籤的本文內容。
在下面的例子中,Duke's Bookstore應用程式頁面showcart.jsp使用Struts logic:present標籤清除購物車,並且如果請求包含一個名為Clear的參數就列印一個訊息。
<logic:present parameter="Clear">
<% cart.clear(); %>
<font color="#ff0000" size="+2"><strong>
You just cleared your shopping cart!
</strong><br> <br></font>
</logic:present>
選擇用屬性或者本文傳遞資訊
正如最後兩節中所展示的,可以將給定的資料作為標籤的屬性或者標籤的本文傳遞。一般來說,任何簡單字串或者可以由對簡單運算式判斷而產生的資料最好作為屬性傳遞。
定義指令碼變數的標籤
自訂標籤可以定義可在頁面中的指令碼中使用的變數。下面的例子展示了如何定義並使用包含一個從JNDI查詢中返回的對象的指令碼變數。這種對象的例子包括企業bean、事務、資料庫、環境項等等:
<tt:lookupegin(); %> id="tx" type="UserTransaction"
name="java:comp/UserTransaction" />
<% tx.b
在Duke's Bookstore應用程式中,有幾個頁面使用了Struts的面向bean的標籤以定義指令碼變數。例如,bookdetails.jsp使 用了bean:parameter標籤以建立指令碼變數bookId並設定它並將它設定為請求參數bookId的值。jsp:setProperty語句還 設定bookDB對象的bookId屬性為請求參數bookId的值。bean:define標籤提取書店資料庫bookDetails屬性 bookDetails的值並將結果定義為指令碼變數book:
<bean:parameter id="bookId" name="bookId" />
<jsp:setProperty name="bookDB" property="bookId"/>
<bean:define id="book" name="bookDB" property="bookDetails"
type="database.BookDetails"/>
<h2><jsp:getProperty name="book" property="title"></h2>
操作標籤
自訂標籤可以通過共用對象彼此合作。
在下面的例子中,tag1建立了一個名為obj1的對象,再由tag2返回這個對象。
<tt:tag1 attr1="obj1" value1="value" />
<tt:tag2 attr1="obj1" />
在下面的例子中,由一組嵌套標籤中的外圍標籤建立的對象對於所有內部標籤都是可用的。因為沒有為對象命名,所以可以減少潛在的命名衝突。這個例子展示在JSP頁面中一組協作的嵌入標籤會是什麼樣子的。
<tt:outerTag>
<tt:innerTag />
</tt:outerTag>
Duke's Bookstore頁面template.jsp使用了一組協作標籤定義應用程式的螢幕。在模板標籤庫中描述了這些標籤。
定義標籤
要定義標籤,需要:
· 為該標籤開發一個tag handler和helper類
· 在標籤庫描述符中聲明這個標籤
本節描述標籤handler和TLD的屬性,並解釋如何為在前面幾節中介紹的標籤開發tag handler和庫描述符元素。
標籤handler
標籤handler是由Web容器調用的一個對象,用於執行帶有自訂標籤的JSP頁面時對這個標籤進行判斷。標籤handler必須實現Tag或者BodyTag介面。介面可以用於接受現有Java對象並使它成為標籤handler。對於新建立的處理器,可以用TagSupport和BodyTagSupport類作為基類。這些類和介面包含在javax.servlet.jsp.tagext包中。
JSP 頁面的servlet在對標籤處理的不同階段調用由Tag和BodyTag介面定義的標籤handler。遇到自訂標籤的開始標籤時,JSP頁面的 servlet調用方法以初始化相應的handler,然後調用handler的doStartTag方法。遇到自訂標籤的結束標籤時,調用處理器的 doEndTag方法。在標籤handler需要與標籤的本文互動時調用其他方法,見帶本文的標籤。為了提供標籤handler的實現,必須實現在處理標籤的不同階段調用的方法,在表16-1中匯總了這些方法。
表16-1標籤handler方法 |
標籤handler類型 |
方法 |
簡單 |
doStartTag, doEndTag, release |
屬性 |
doStartTag, doEndTag, set/getAttribute1...N, release |
本文、判斷且無互動 |
doStartTag, doEndTag, release |
本文、迭代判斷 |
doStartTag, doAfterBody, doEndTag, release |
本文、互動 |
doStartTag, doEndTag, release, doInitBody, doAfterBody, release |
標籤handler可以使用一個能讓它得以與JSP頁面通訊的API。到API的進入點是頁面內容物件(javax.servlet.jsp.PageContext),通過它標籤handler可以擷取JSP頁面能夠訪問的所有其他隱式對象(請求、會話和應用程式)。
隱式對象可以有與其相關聯的命名屬性。可以用[set|get]Attribute方法訪問這種屬性。
如果標籤是嵌入的,標籤handler還可以訪問與外圍標籤關聯的handler稱為parent)。
一組相關的標籤handler類(標籤庫)一般是打包的且作為JAR文檔部署。
標籤庫描述符
標籤庫描述符(TLD)是一個描述標籤庫的XML文檔。TLD包含有關整個庫以及庫中包含的每一個標籤的資訊。Web容器用TLD驗證標籤,JSP頁面開發工具也使用TLD。
TLD檔案名稱必須有副檔名.tld。TLD檔案也儲存在WAR檔案的WEB-INF目錄中或者在WEB-INF的子目錄中。
TLD必須以指定XML的版本和文件類型定義(DTD)的XML文檔序言(prolog)開始。
<?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
Tomcat支援版本 1.1和1.2的DTD。不過,本章所討論的是1.2版本,因為在開發的所有標籤庫中都應該使用最新的版本。模板庫TLDtutorial-template.tld符合版本1.2。Struts庫TLD符合版本1.1的DTD,它的元素要少,且其中一些元素使用了稍微不同的名字。
TLD的根是taglib元素。表16-2中列出了taglib的子項目:
表16-2 taglib子子項目 |
元素 |
說明 |
tlib-version |
標籤庫的版本 |
jsp-version |
這個標籤庫要求的JSP規範版本 |
short-name |
JSP頁面編寫工具可以用來建立助記名的可選名字 |
uri |
唯一標識該標籤庫的的URI |
display-name |
將由工具顯示的可選名 |
small-icon |
將由工具使用的可選小表徵圖 |
large-icon |
可被工具使用的可選大表徵圖 |
description |
可選的標籤特定資訊 |
listener |
見listener元素 |
tag |
見tag元素 |
listener元素
標籤庫可以指定一些事件監聽器類(見處理Servlet生命週期事件)。 這些監聽器在TLD中作為listener元素列出,Web容器將初始化監聽器類並以類似在WAR級定義的監聽器的方式註冊它們。與WAR級監聽器不同, 這裡沒有指定標籤庫監聽器註冊的順序。listener元素的唯一子項目是listener-class元素,它必須包含監聽類的完全限定名。
tag元素
庫中的每一個標籤都由給出其名字和其標籤handler的類、在由標籤建立的指令碼變數上的資訊以及標籤屬性上的資訊描述。指令碼變數資訊可以在TLD中直接給出,也可以通過tag extra info類給出(見定義指令碼變數的標籤)。每一個屬性聲明包含指明屬性是否是必需的、其值是否可以由請求時運算式確定以及屬性類型的內容(見屬性元素)。
在tag元素中的TLD中指定標籤。在表16-3中出了tag的子項目:
表16-3 標籤子項目 |
元素 |
說明 |
name |
唯一標籤名 |
tag-class |
標籤handler類的完全限定名 |
tei-class |
javax.servlet.jsp.tagext.TagExtraInfo的可選子類。見提供有關指令碼變數的資訊。 |
body-content |
本文內容類型。見body-conten元素和 body-content元素。 |
display-name |
由工具顯示的可選名 |
small-icon |
可以由工具使用的小表徵圖 |
large-icon |
可以由工具使用的大表徵圖 |
description |
可選的標籤特定的資訊 |
variable |
可選的指令碼變數資訊。見提供有關指令碼變數的資訊。 |
attribute |
標籤屬性資訊。見Attribute 元素。 |
下面幾節描述開發在標籤類型中介紹的每一種類型的標籤所需要的方法和TLD。
簡單標籤 標籤handler
簡 單標籤的handler必須實現Tag介面的doStartTag和doEndTag方法。在遇到開始標籤時調用doStartTag方法。因為簡單標籤 沒有本文,所以這個方法返回SKIP_BODY。在遇到結束標籤時調用doEndTag方法。如果要對頁面的其他部分進行判斷,則doEndTag方法需 要返回EVAL_PAGE,否則,它就返回SKIP_PAGE。
在第一節討論的簡單標籤
<tt:simple />
由下欄標籤handler實現:
public SimpleTag extends TagSupport {
public int doStartTag() throws JspException {
try { pageContext.getOut().print("Hello.");
} catch (Exception ex) {
throw new JspTagException("SimpleTag: " +
ex.getMessage()); }
return SKIP_BODY; }
public int doEndTag() {
return EVAL_PAGE;
}
}
body-content元素
沒有本文的標籤必須用body-content元素宣告它們的本文內容是空的:
<body-content>empty</body-content>