posts - 51, comments - 17, trackbacks - 0, articles - 9
            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

          Jsp 自定義標(biāo)簽

          Posted on 2007-06-26 19:19 chenweicai 閱讀(1954) 評論(0)  編輯  收藏
          本教程目的 第 1 頁(共3 頁)
                                                                                                  

          想要在 JavaServer Pages (JSP) 應(yīng)用程序中添加自定義標(biāo)簽嗎?本教程將為您展示如何用這些標(biāo)簽編寫類似于 JSP 技術(shù)自帶操作 —— 如 jsp:useBeanjsp:getPropertyjsp:forward —— 的自定義操作。介紹如何用特定于自已的域的表示邏輯的自定義操作來擴展 JSP 語法。

          在 JSP 應(yīng)用程序中添加 自定義標(biāo)簽 的能力可以使您將工作重點放到以文檔為中心的開發(fā)方式上。可以使 Java 代碼不出現(xiàn)在 JSP 頁中,從而使這些頁面更容易維護。(我從經(jīng)驗中學(xué)到,在 JSP 頁中放入過多的 Java 代碼時,代碼維護就會成為可怕的任務(wù))。本教程將使您可以立即開發(fā)出自定義標(biāo)簽。了解了 JSP 自定義標(biāo)簽開發(fā)的好處后,您可能會對程序員沒有更多地使用它而感到意外。

          在本教程中,我將討論使用自定義標(biāo)簽的基本內(nèi)容。將介紹如何用自定義標(biāo)簽創(chuàng)建可重用的表示組件并避免在 JSP 頁加入 Java scriptlet。

          在本教程中,我們將:

          • 定義一個 JSP 自定義標(biāo)簽體系結(jié)構(gòu)。
          • 解釋簡單標(biāo)簽。
          • 定義嵌套標(biāo)簽。
          • BodyContent 解釋標(biāo)簽。
          • 在標(biāo)簽中添加屬性。
          • 在標(biāo)簽中添加 scriptlet 變量。
          • 用自定義標(biāo)簽實現(xiàn)控制流程。
          • 用 Struts 簡化標(biāo)簽部署。

          我要學(xué)習(xí)本教程嗎? 第 2 頁(共3 頁)


          如果發(fā)現(xiàn)自己在 JSP 應(yīng)用程序中加入了大量 Java scriptlet,那么本教程就是為您準(zhǔn)備的。 閱讀本教程后,就會掌握將 Java 代碼從 JSP 頁面中清除出去所需要的信息。

          本教程假定讀者熟悉 Java 平臺、JavaServer Pages (JSP) 技術(shù)、MVC 模式、Reflection API、Model 2,最好還有 Struts 框架。此外,要從本教程中得到最大的收獲,還需要很好的使用標(biāo)簽庫的經(jīng)驗

          關(guān)于作者 第 3 頁(共3 頁)


          Rick Hightower 是一位 J2EE 開發(fā)人員和顧問,他熱衷于使用 J2EE、Ant、Hibernate、Struts、IMB 的 ETTK 和 Xdoclet。 Rick 是 Trivera Technologies 的前任 CTO,這是一家全球培訓(xùn)、指導(dǎo)和咨詢公司,其重點是企業(yè)開發(fā)。他經(jīng)常在 IBM developerWorks 上發(fā)表文章,并編寫了 10 多篇 developerWorks 教程,內(nèi)容從 EJB 技術(shù)到 Web 服務(wù)到 XDoclet。 Rick 不久前與別人共同開辦了另一家名為 ArcMind 的公司,它專門研究 agile 方法,還從事 Struts/JavaServer Faces 開發(fā)、咨詢和指導(dǎo)。

          在為 eBlox 工作時,Rick 和 eBlox 小組遠在 1.0 版本之前就已使用 Struts 為電子商務(wù)站點構(gòu)建了兩個框架和一個 ASP (應(yīng)用程序服務(wù)提供者)。這個框架目前正在為 2000 多個在線商店店面提供支持。

          Rick 最近完成了一本名為 Professional Jakarta Struts 的書。在周游全國對 J2EE 和 Struts 項目提供咨詢,或者在大會上發(fā)表關(guān)于 J2EE 和極端編程 (extreme programing)的講演之余,Rick 喜歡在通宵咖啡店喝咖啡,寫一些有關(guān) Struts、J2EE 和其他內(nèi)容的文章,并以第三人稱描寫他自己。

          標(biāo)簽處理程序 第 1 頁(共2 頁)


          在創(chuàng)建自定義標(biāo)簽之前,需要創(chuàng)建一個 標(biāo)簽處理程序。標(biāo)簽處理程序是一個執(zhí)行自定義標(biāo)簽操作的 Java 對象。在使用自定義標(biāo)簽時,要導(dǎo)入一個 標(biāo)簽庫 —— 即一組標(biāo)簽/標(biāo)簽處理程序?qū)ΑMㄟ^在 Web 部署描述符中聲明庫導(dǎo)入它,然后用指令 taglib 將它導(dǎo)入 JSP 頁。

          如果 JSP 容器在轉(zhuǎn)換時遇到了自定義標(biāo)簽,那么它就檢查 標(biāo)簽庫描述符(tag library descriptor) (TLD) 文件以查詢相應(yīng)的標(biāo)簽處理程序。TLD 文件對于自定義標(biāo)簽處理程序,就像 Web 部署描述符對于 servlet 一樣。

          在運行時,JSP 頁生成的 servlet 得到對應(yīng)于這一頁面所使用的標(biāo)簽的標(biāo)簽處理程序的一個實例。生成的 servlet 用傳遞給它的屬性初始化標(biāo)簽處理程序。

          標(biāo)簽處理程序?qū)崿F(xiàn)了 生存周期 方法。生成的 servlet 用這些方法通知標(biāo)簽處理程序應(yīng)當(dāng)啟動、停止或者重復(fù)自定義標(biāo)簽操作。生成的 servlet 調(diào)用這些生存周期方法執(zhí)行標(biāo)簽的功能。

          標(biāo)簽的類型 第 2 頁(共2 頁)


          可以定義兩種類型的標(biāo)簽:

          • javax.servlet.jsp.tagext.Tag
          • javax.servlet.jsp.tagext.BodyTag

          正文 進行操作 —— 即對在開始和結(jié)束標(biāo)簽之間的內(nèi)容進行操作的 —— 標(biāo)簽必須實現(xiàn) BodyTag 接口。在這個教程中,我們將稱這些標(biāo)簽為 正文標(biāo)簽。我們將不對其正文操作的標(biāo)簽稱為 簡單標(biāo)簽。簡單標(biāo)簽可以實現(xiàn) Tag 接口,盡管不要求它們這樣做。要記住不對其正文操作的標(biāo)簽仍然 正文,只不過,它的標(biāo)簽處理程序不能讀取這個正文。

          簡單標(biāo)簽的例子 第 1 頁(共9 頁)
                                                                                                             上一頁      下一頁

          Struts 框架帶有幾個自定義標(biāo)簽庫(有關(guān) Struts 的更多信息的鏈接請參閱 參考資料 )。這些庫中的一個標(biāo)簽可以創(chuàng)建一個支持改寫 URL 的鏈接并用 jsessionid 對改寫的連接編碼。

          不過有一個問題:如果希望傳遞一組請求參數(shù)(如查詢字符串),也許必須為此創(chuàng)建一個 Java scriptlet。真是亂!下面的清單 (search_results.jap) 展示了一個 JSP 頁,它被迫加入了這樣一個 scriptlet。

           <%@ taglib uri="struts-html" prefix="html" %> <jsp:useBean class="java.util.HashMap" id="deleteParams" /> <% deleteParams.put("id", cd.getId()); deleteParams.put("method","delete"); %> <!-- Pass the map named deleteParams to html:link to generate the request parameters--> <html:link action="/deleteCD" name="deleteParams">delete </html:link> </font></td>  

          search_results.jsp 創(chuàng)建一個 hashmap 并向這個 map 傳遞兩個屬性。在下面幾小節(jié),我們將創(chuàng)建一個不用 Java 代碼完成這項工作的自定義標(biāo)簽。我們的標(biāo)簽將定義如下的一個 hashmap:

           <map:mapDefine id="deleteParams"> <map:mapEntry id="id" name="cd" property="id"/> <map:mapEntry id="method" value="delete"/> </map:mapDefine> <!-- Pass the map named deleteParams to html:link to generate the request parameters--> <html:link action="/deleteCD" name="deleteParams">delete </html:link> </font></td>  

          這將使我們可以容易地創(chuàng)建小型 map。

          這個例子將展示幾個關(guān)鍵概念,包括使用嵌套標(biāo)簽和定義 scriplet 變量。首先我將解釋這個標(biāo)簽是如何工作的。然后在以后的幾節(jié)中建立這些概念,并介紹如何編寫這個標(biāo)簽的不同形式,使它們處理其正文并控制執(zhí)行流程。

          構(gòu)建簡單標(biāo)簽的步驟 第 2 頁(共9 頁)


          讓我們創(chuàng)建一個定義一個 HashMap scriptlet 變量的標(biāo)簽。為此,需要實現(xiàn)標(biāo)簽處理程序接口 (javax.servlet.jsp.tagext.Tag)。因此,我們要創(chuàng)建的第一個標(biāo)簽將是一個簡單標(biāo)簽。

          這個標(biāo)簽將實例化一個 map。使用這個標(biāo)簽的開發(fā)人員可以指定要實例化的 map 的類型 —— HashMapTreeMapFastHashMap 或者 FastTreeMapFastHashMapFastTreeMap 來自 Jakarta Commons Collection library (有關(guān)鏈接請參閱 參考資料)。開發(fā)人員還可以指定標(biāo)簽所在的范圍 —— 頁、請求、會話還是應(yīng)用程序范圍。

          要構(gòu)建這個簡單標(biāo)簽,我們需要完成以下步驟:

          1. 創(chuàng)建實現(xiàn)了 Tag 接口(準(zhǔn)確地說是 javax.servlet.jsp.tagext.Tag)的標(biāo)簽處理程序類。

          2. 創(chuàng)建一個 TLD 文件。

          3. 在標(biāo)簽處理程序 Java 類中創(chuàng)建屬性。

          4. 在 TLD 文件中定義與標(biāo)簽處理程序 Java 類中定義的屬性對應(yīng)的屬性。

          5. 在 TLD 文件中聲明 scriptlet 變量。

          6. 實現(xiàn) doStartTag() 方法。在標(biāo)簽處理程序類中,根據(jù)屬性將值設(shè)置到 scriptlet 變量中。

          如果您像我一樣,可能會提前閱讀書的結(jié)尾,所以請查看 附錄 中標(biāo)簽處理程序類的完整列表以了解這個過程是如何結(jié)束的。

          在下面幾小節(jié)中,我們將分析 MapDefineTag 的實現(xiàn),并分析如何到達這一步。

          第 1 步:創(chuàng)建一個實現(xiàn)了 Tag 接口的標(biāo)簽處理程序 第 3 頁(共9 頁)


          為了編寫標(biāo)簽處理程序,必須實現(xiàn) Tag 接口。如前所述,這個接口用于不操縱其標(biāo)簽正文的簡單標(biāo)簽處理程序。就像 J2EE API 文檔 (有關(guān)鏈接請參閱 參考資料)所說的:Tag 接口定義了標(biāo)簽處理程序和 JSP 頁實現(xiàn)類之間的基本協(xié)議。它定義了在標(biāo)簽開始和結(jié)束時調(diào)用的生存周期和方法。

          標(biāo)簽處理程序接口有以下方法:

          方法 作用
          int doStartTag() throws JspException 處理開始標(biāo)簽
          int doEndTag() throws JspException 處理結(jié)束標(biāo)簽
          Tag getParent()/void setParent(Tag t) 獲得/設(shè)置標(biāo)簽的父標(biāo)簽
          void setPageContext(PageContext pc) pageContext 屬性的 setter 方法
          void release() 釋放獲得的所有資源

          TagSupport

          現(xiàn)在,不必直接實現(xiàn) Tag 接口,相反,用 map 定義的(map-defining)標(biāo)簽將繼承 TagSupport 類。這個類以有意義的默認方法實現(xiàn) Tag 接口,因而使開發(fā)自定義標(biāo)簽更容易 (有關(guān) TagSupport 的 API 文檔的鏈接請參閱 參考資料)。 例如,TagSupport 類定義了 get/setParent()setPageContext(),這與所有標(biāo)簽處理程序幾乎相同。 get/setParent() 方法允許標(biāo)簽嵌套。TagSupport 類還定義了一個可以被子類使用的 pageContext 實例變量 (protected PageContext pageContext),這個變量是由 setPageContext() 方法設(shè)置的。

          在默認情況下,TagSupport 實現(xiàn)了 doStartTag() 以使它返回 SKIP_BODY 常量,表示將不對標(biāo)簽正文進行判斷。 此外,在默認情況下,doEndTag() 方法返回 EVAL_PAGE,它表示 JSP 運行時引擎應(yīng)當(dāng)對頁面的其余部分進行判斷。 最后,TagSupport 實現(xiàn)了 release(),它設(shè)置 pageContext 及其父元素為 null

          TagSupport 類還實現(xiàn)了 IterationTag 接口和 doAfterBody(),這樣它就返回 SKIP_BODY。 在后面討論進行迭代的標(biāo)簽時我將對此加以更詳細的解釋(請參閱 用自定義標(biāo)簽控制流程)。

          好了,現(xiàn)在讓我們通過繼承 TagSupport 來實現(xiàn) Tag 接口:

           ... import javax.servlet.jsp.tagext.TagSupport; ... public class MapDefineTag extends TagSupport { ...  

          我們已經(jīng)定義了標(biāo)簽處理程序,現(xiàn)在需要增加從處理程序到 TLD 文件中的標(biāo)簽的映射。我們將在下一小節(jié)中對此進行處理。然后,將完成 MapDefineTag 中剩余的代碼。

          第 2 步:創(chuàng)建一個 TLD 文件 第 4 頁(共9 頁)


          TLD 文件對自定義標(biāo)簽處理程序的作用就像 Web 部署描述符對 servlet 的作用。 TLD 文件列出了從標(biāo)簽名到標(biāo)簽處理程序的映射。 這個文件中的大多數(shù)數(shù)據(jù)都是在 JSP 頁轉(zhuǎn)換時使用的。 TLD 文件通常保存在 Web 應(yīng)用程序的 WEB-INF 目錄,并在 web.xml 文件中聲明。它們一般用 .tld 擴展名結(jié)束。

          TLD 文件有一個 導(dǎo)言(preamble),在這里標(biāo)識 JSP 技術(shù)的版本和使用的標(biāo)簽庫。這個導(dǎo)言通常看起來像這樣:

           <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd"> <taglib> <tlib-version>1.0</tlib-version> <jsp-version>1.2</jsp-version> <short-name>map</short-name>  

          讓我們更詳細地分析一下這些標(biāo)簽:

          • TLD 文件的根元素是 taglibtaglib 描述了一個 標(biāo)簽庫 —— 即一組標(biāo)簽/標(biāo)簽處理程序?qū)Α?

          • 因為我們使用的是 JSP 版本 1.2,所以在這個例子中需要 tlib-versionshort-name 元素。

          • tlib-version 元素對應(yīng)于標(biāo)簽庫版本。

          • jsp-version 對應(yīng)于標(biāo)簽庫所依賴的 JSP 技術(shù)的版本。

          • short-name 元素定義了 IDE 和其他開發(fā)工具可以使用的標(biāo)簽庫的簡單名。

          • taglib 元素包含許多 tag 元素,標(biāo)簽庫中每一個標(biāo)簽有一個 tag 元素。

          因為我們剛創(chuàng)建了自己的類,所以我們將繼續(xù)往下進行,在 TLD 文件中聲明這個類,如下所示:

           <taglib> 
          ...
          <tag>
          <name>mapDefine</name>
          <tag-class>trivera.tags.map.MapDefineTag</tag-class>
          <body-content>JSP</body-content>
          ...

          tag 元素用于將自定義標(biāo)簽映射到它們的自定義標(biāo)簽處理程序。上述清單中的 tag 元素將自定義標(biāo)簽 mapDefine 映射到處理程序 trivera.tags.map.MapDefineTag。 因此,不論在 mapDefine 上運行的是什么轉(zhuǎn)換引擎,都會調(diào)用 trivera.tags.map.MapDefineTag

          已經(jīng)在 TLD 中定義了標(biāo)簽,接下來要在標(biāo)簽處理程序類中定義這個標(biāo)簽的一些屬性了。

          第 3 步:在標(biāo)簽處理程序 Java 類中創(chuàng)建屬性 第 5 頁(共9 頁)
                                                                                                                         上一頁      下一頁

          我們希望為 mapDefine 標(biāo)簽指定三個屬性,如下所示:

          屬性說明
          id 新 scriptlet 變量的名字。
          scope 新 scriptlet 變量所在的范圍。
          type 新 scriptlet 變量的類型 (HashMapFastHashMapTreeMap 或者 FastTreeMap)。 如果 type 設(shè)置為 hash,那么就會創(chuàng)建一個 HashMap。如果 type 設(shè)置為 fasthash,那么將創(chuàng)建 FastHashMap

          在 JSP 頁中使用這個標(biāo)簽時,它看起來將像下面這樣:

           <map:mapDefine id="editParams" scope="session" type="hash"> 
          ...
          </map:mapDefine>

          這個標(biāo)簽將在會話范圍內(nèi)創(chuàng)建一個名為 editParamsHashMap

          為了在標(biāo)簽處理程序中創(chuàng)建屬性,需要定義相應(yīng)的 JavaBean 屬性。 因此,每一個屬性在標(biāo)簽處理程序中都有對應(yīng)的 setter 方法,如下所示:

           public class MapDefineTag extends TagSupport { 
          ...
          private String type = FASTTREE;
          private String id;
          private String scope;


          public void setType(String string)
          { type = string; }

          public void setId(String string)
          { id = string; }
          public void setScope(String string)
          { scope = string; }

          轉(zhuǎn)換引擎將用硬編碼的配置數(shù)據(jù)或者運行時表達式設(shè)置這個標(biāo)簽的屬性。 我們將在 第 4 步:在 TLD 文件中定義屬性 中對此做更詳細的討論。

          第 5 步:實現(xiàn) doStartTag() 方法 中,我們將在標(biāo)簽處理程序的 doStartTag() 方法中使用這些屬性。

          第 4 步:在 TLD 文件中定義屬性 第 6 頁(共9 頁)


          就 像上一小節(jié)中所做的那樣,通過聲明 JavaBean 屬性定義自定義屬性,然后在 TLD 文件中聲明這些屬性。 每一個 JavaBean 屬性都必須與相應(yīng)的自定義標(biāo)簽屬性相匹配。 在 TLD 中定義的屬性必須匹配 JavaBean 屬性,不過卻可以有與標(biāo)簽屬性不匹配的 JavaBean 屬性。

          下面是 MapDefineTag 的屬性聲明:

           <tag> <name>mapDefine</name> <tag-class>trivera.tags.map.MapDefineTag</tag-class> <body-content>JSP</body-content> ... <attribute> <name>id</name> <required>true</required> <rtexprvalue>false</rtexprvalue> <description>The id attribute</description> </attribute> <attribute> <name>scope</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description>The scope attribute</description> </attribute> <attribute> <name>type</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description> Specifies the type of map valid values are fasttree, fasthash, hash, tree </description> </attribute> </tag>  

          name 元素指定屬性的名字。required 元素指定屬性是否是必需的(默認值是 false)。rtexprvalue 元素表明屬性是硬編碼了轉(zhuǎn)換時的值還是允許使用運行時 scriptlet 表達式。

          記住,MapDefineTag 類必須為前面描述的每一個屬性定義一個 JavaBean 屬性,我們在 第 3 步:在標(biāo)簽處理程序 Java 類中創(chuàng)建屬性 中完成這個任務(wù)。

          第 5 步:實現(xiàn) doStartTag() 方法 第 7 頁(共9 頁)


          標(biāo)簽開始時調(diào)用 doStartTag() 方法 —— 從開發(fā)人員的角度看,這是當(dāng)引擎遇到 <map:mapDefine> 時發(fā)生的。如果 doStartTag() 返回 SKIP_BODY,那么將不處理標(biāo)簽正文。 如果它返回一個 EVAL_BODY_INCLUDE,那么將處理正文。

          MapDefine 類的 doStartTag() 方法完成以下工作:

          • 根據(jù) type 屬性確定要創(chuàng)建的 map 的屬性。
          • 根據(jù) scope 屬性確定新的 map 對象放在什么范圍內(nèi)。
          • 根據(jù) id 屬性確定新 map 對象要放入的范圍的名字。

          讓我們更詳細地分析這個過程。MapDefine 類檢查 type 屬性是設(shè)置為 FASTTREEHASHTREE 還是 FASTHASH。然后創(chuàng)建相應(yīng)的 map,如下所示:

           /* String constants for the different types of maps we support */ public static final String FASTHASH = "FASTHASH"; public static final String FASTTREE = "FASTTREE"; public static final String HASH = "HASH"; public static final String TREE = "TREE"; /** The map we are going to create */ private Map map = null; /** The member variable that holds the type attribute */ private String type = FASTTREE; ... public int doStartTag() throws JspException { /** Based on the type attribute, determines which type of Map to create */ if (type.equalsIgnoreCase(FASTTREE)) { map = new FastTreeMap(); } else if (type.equalsIgnoreCase(HASH)) { map = new HashMap(); } else if (type.equalsIgnoreCase(TREE)) { map = new TreeMap(); } else if (type.equalsIgnoreCase(FASTHASH)) { map = new FastHashMap(); }  

          然后,用 idscope 屬性將 hashmap 以一個給定的名字設(shè)置到一個給定范圍中:

           private String id; private String scope; public int doStartTag() throws JspException { ... if (scope == null){ pageContext.setAttribute(id, map); }else if("page".equalsIgnoreCase(scope)){ pageContext.setAttribute(id, map); }else if("request".equalsIgnoreCase(scope)){ pageContext.getRequest().setAttribute(id, map); }else if("session".equalsIgnoreCase(scope)){ pageContext.getSession().setAttribute(id, map); }else if("application".equalsIgnoreCase(scope)){ pageContext.getServletContext().setAttribute(id, map); } return EVAL_BODY_INCLUDE; }  

          如果范圍屬性是 null,那么 map 將放入頁范圍。否則,參數(shù)將放入通過 scope 屬性傳遞的范圍名所對應(yīng)的范圍中。

          到目前為止,我們已經(jīng)有一個非常簡單的標(biāo)簽,它有三個屬性:idscopetype。 我們將用給定的名字將 map 放到一個范圍中。但是,我們還有一件事沒做,就是聲明 scriptlet 變量。 為了做到這一點,需要向 TLD 再添加一項,這就是我們在下一小節(jié)中所要做的事。


          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 饶平县| 铅山县| 长宁县| 旌德县| 修水县| 车险| 龙里县| 招远市| 安丘市| 信宜市| 措美县| 大荔县| 昭苏县| 平安县| 忻州市| 赤城县| 神木县| 蒙山县| 九寨沟县| 石门县| 灌南县| 富裕县| 乌拉特中旗| 闵行区| 兴化市| 无为县| 曲松县| 延吉市| 彰化市| 堆龙德庆县| 潞西市| 临城县| 河池市| 湟源县| 黑龙江省| 邻水| 内丘县| 台南市| 汉沽区| 新平| 子洲县|