在JSP最佳實踐的上一期,您學習了一種基於scriptlet的技術,這種技術被用來將上次修改的時間戳添加到JavaServer Page(JSP)文件中。不幸的是,比起它所提供的短期利益,scriptlet會將更多的長期複雜性引入到您的頁面中來。這些scriptlet會用Java代碼將各種類型的HTML混雜在一起,從而使得 程式的調試和設計極其錯綜複雜。scriptlet不能重用,這常常導致開發者不得不在JSP頁面之間進行複製-粘貼操作,進而導致同一段代碼出現多個版本。而且,scriptlet還加大了錯誤處理的難度,因為JSP沒有提供乾淨利落的方式來報告腳本錯誤。
因此,這次我們將設計一種新的解決方案。在本期的JSP最佳實踐中,您將學習一些基礎知識,主要是關於如何將scriptlet轉換成自定義標記,並對其進行設置以便在您的JSP開發項目中使用。
為什麼使用taglib?
所謂標記庫(tag library),是指由在JSP頁面中使用的標記所組成的庫。JSP容器推出時帶有一個小型的、默認的標記庫。而自定義標記庫是人們為了某種特定的用途或者目的,將一些標記放到一起而形成的一種庫。在一個團隊中協同工作的開發者們可能會為各自的項目創建一些非常特定化的自定義標記庫,同時也會創建一個通用自定義標記庫,以供當前使用。
JSP 標記替代了scriptlet,並緩解了由scriptlet所招致的所有令人頭痛的事情。例如,您可以看到這樣的標記:
<store:shoppingCart id="1097629"/> |
或者這樣的標記:
<tools:usageGraph /> |
每個標記都包含了指向一個Java類的引用,但是類中的代碼仍然在它該在的地方:在標簽之外,一個編譯好的類文件之中。
從 scriptlet 到標記
創建一個自定義標記的第一步就是決定您想怎樣使用它,如何稱呼它,以及它允許使用或者需要什麼屬性(如果有的話)。對於時間戳標記,我們所需要的很簡單:只要一個能夠輸出一個頁面的最後修改數據的簡單標記。
因為不需要屬性,這個標記看上去就是這個樣子:
<site-utils:lastModified /> |
這個標記的名稱和前綴是一樣的:都是site-utils。元素的內容是空的,這意味著該元素中不允許有子元素存在。定義了這個標記之後,接下來的一步就是實現它的行為。
實現行為
實現標記行為的第一步是將scriptlet代碼從原先所在的地方移到一個Java類(LastModifiedTag)中,如清單 1 所示:
清單 1. 創建一個時間戳標記
package com.newInstance.site.tags;
import java.io.File; import java.io.IOException; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import javax.servlet.http.HttpServletRequest; import javax.servlet.jsp.tagext.TagSupport;
public class LastModifiedTag extends TagSupport {
public int doEndTag() { try { HttpServletRequest request = (HttpServletRequest)pageContext.getRequest(); String path = pageContext.getServletContext().getRealPath( request.getServletPath()); File file = new File(path);
DateFormat formatter = DateFormat.getDateInstance( DateFormat.LONG);
pageContext.getOut().println( formatter.format(new Date(file.lastModified()))); } catch (IOException ignored) { } return EVAL_PAGE; } } |
這個方法中的代碼看上去比較熟悉;實質上,它正是我們在先前用到的相同的時間戳代碼。由於不需要用戶輸入,而且該標記也沒有屬性或者嵌入的內容,我們惟一需要關心的一個新方法就是doEndTag(),在這個方法中該標記可以輸出內容(在這個例子中是最後修改數據)到JSP頁面。
清單 1 中其他的更改更多地與作為一個JSP標記的代碼有關,而與在一個頁面內運行的scriptlet沒有多大關係。例如,所有的JSP標記都應該擴展JSP類javax.servlet.jsp.tagext.TagSupport,這個類為JSP標記提供了基本框架。可能您還注意到 ,該標記返回的 EVAL_PAGE. EVAL_PAGE 是一個預定義的整型常量,它指示容器處理頁面的剩下部分。另一種選項就是使用SKIP_PAGE,它將中止對頁面剩下部分的處理。如果您要將控制轉移到另一個頁面,例如您要前進(forward)或者重定向(redirect)用戶,那麼只需要使用SKIP_PAGE。剩下來的細節都是與時間戳自身有關的事情。
接下來,編譯這個類,並將 LastModifiedTag.class 文件放到一個WEB-INF/classes 目錄下,注意要放到正確的路徑層次結構中。這個路徑應該匹配該標記的包名,包名中的圓點(.)用斜杠(/)代替。在本例中,目錄的路徑是基路徑(WEB-INF/classes)再加上層次結構com/newInstance/site/tags。如果有一個名為 foo.bar.tag.MyTag的標記,那麼它將被放在 WEB-INF/classes/foo/bar/tag中。這種路徑層次結構確保了Web容器在任何需要裝載該標記的時候都能夠找到這個類。
創建TLD
接下來的一步是創建一個標記庫描述符(tag library descriptor ,TLD)文件。TLD向容器和任何要使用該標記庫的JSP頁面描述您的標記庫。清單 2 顯示了一個非常標準的TLD,其中只包含了一個標記。當您將更多的標記添加到庫中時,TLD文件的長度和複雜性將隨之增長。
清單 2. 一個標記庫描述符
<?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">
<taglib> <tlib-version>1.0</tlib-version> <jsp-version>1.2</jsp-version> <short-name>site-utils</short-name> <uri>http://www.newInstance.com/taglibs/site-utils</uri>
<tag> <name>lastModified</name> <tag-class>com.newInstance.site.tags.LastModifiedTag</tag-class> <body-content>empty</body-content> </tag> </taglib> |
TLD 文件頂部的資訊應用於整個標記庫。在本例中,我提供了一個版本(這對於跟蹤某個標記庫的JSP創建者擁有哪個版本很有用,尤其是在您需要經常修改標記庫的情況下更是如此);該標記庫所依賴的JSP版本;一個為該標記庫推薦的前綴;以及用於引用這個標記庫的URI。注意,我使用了前綴short-name作為URI的一部分,這樣 比較容易將前綴和標記庫的URI看成一個整體。
剩下的資訊用於一個特定的標記,這些資訊用 tag 元素表示。我指定了該標記的名稱、用於該標記的類(這個類應該被編譯好並放在適當的地方,以便容器能夠裝載),最後還指定了該標記是否有嵌入的內容。在本例中,標記沒有嵌入的內容,因此使用"empty"。
保存這個文件,並將其放到WEB-INF/tlds目錄下(您可能需要在您的容器中創建這個目錄)。我將這個文件保存為site-utils.tld,並在該標記庫的URI(推薦的前綴)和TLD文件本身之間再次創建一個乾淨的鏈結。對於這個特定的標記庫,要使其可以使用,最後一步要做的是讓您的Web應用知道如何連接一個JSP頁面中的URI,如何請求使用一個標記庫。這可以通過應用的web.xml文件來做。清單 3 顯示了一個非常簡單的web.xml片段,正是 它為我們的標記庫做這樣的事情。
清單 3. 將一個URI與一個標記庫鏈結起來
<taglib> <taglib-uri>http://www.newInstance.com/taglibs/site-utils</taglib-uri> <taglib-location>/WEB-INF/tlds/site-utils.tld</taglib-location> </taglib> |
包裝起來
如果您已經按照上述步驟執行了,那麼現在您應該能夠在JSP頁面中引用新標記了。清單 4 向我們展示了新改進的footer.jsp,這個文件中現在完全沒有scriptlet,也沒有指向具有scriptlet的JSP頁面的引用。
清單 4. 使用新的標記庫
<%@ taglib prefix="site-utils" uri="http://www.newInstance.com/taglibs/site-utils" %> </td> <td width="16" align="left" valign="top"> </td> </tr> <!-- End main content -->
<!-- Begin footer section --> <tr> <td width="91" align="left" valign="top" bgcolor="#330066"> </td> <td align="left" valign="top"> </td> <td class="footer" align="left" valign="top"><div align="center"><br> © 2003 <a href="mailto:webmaster@newInstance.com">Brett McLaughlin</a><br> Last Updated: <site-utils:lastModified /> </div></td> <td align="left" valign="top"> </td> <td width="141" align="right" valign="top" bgcolor="#330066"> </td> </tr> </table> <!-- End footer section --> |
在前面幾期中看了JSTL如何工作(參見“利用JSTL更新您的JSP頁面”和“導入內容到您的Web站點”)之後,接下來該做什麼您應該很清楚了:我們通過使用web.xml文件中的URI來引用這個標記庫,為之分配一個前綴(來自TLD的 short-name 始終是最好的選擇),然後就像使用任何其他JSP標記一樣使用這個標記。最終得到的是一個簡潔的、更好的JSP頁面,這個JSP頁面運行起來不比有 scriptlet 的時候差。
當前我們的自定義標記還很簡單,在下一期,我們將擴展其功能。到時候網上見。
- 還是JSP技術的初學者嗎?那麼請參見由Brett McLaughlin撰寫的JSP最佳實踐系列前面的幾期。
- 您或許還想研究一下對遵從JSP規範的IDE的使用。那麼下面有一些選擇:
- 在JCP.org網站,您可以找到JSP規範說明書。
- Jeff Wilson的“利用自定義標記控制您的JSP頁面” (developerWorks, 2002年1月) 是關於自定義標記庫通信的入門讀物。
- “使用JSP和JDBC技術的動態的基於Web的數據訪問”(developerWorks, 2001年9月) 演示了對於訪問和操縱JSP頁面中內容必不可少的一些技術。
- Noel Bergman的“JSP taglibs: 設計帶來更好的可用性”(developerWorks,2001年12月) 介紹了自定義標記庫工具,這種工具允許創建JSTL。
- Mark Kolb的“JSTL 初級讀物” (developerWorks, 2003年2月至5月)對JSP標準標記庫(JSP Standard Tag Library)作了完整的介紹。
- 要獲得對JSP技術的指導性的介紹,請參見Noel Bergman的教程“JavaServer Pages 技術簡介”(developerWorks, 2001年8月)。
- 要獲得JSP技術的細節,您最好的選擇是讀JSP規範說明書。
- Hans Bergsten的JavaServer Pages(O'Reilly & Associates, 2002)是學習JSP技術時不可或缺的資料。
- 參見developerWorks Java 技術教程頁面 以獲得完整的免費教程清單。
- 在developerWorks Java 技術專區 您可以找到大量關於Java編程各個方面的文章(包括更多關於JSP技術的文章)。