通過(guò)用戶(hù)定義的屬性擴展標記的功能性
Brett McLaughlin作者, O‘Reilly and Associates
2003 年 9 月 17 日
BrettMcLaughlin 繼續他的 JSP 最佳實(shí)踐系列,這次他將用一個(gè)屬性來(lái)擴展自定義的時(shí)間戳標記( lastModified ),這個(gè)屬性讓頁(yè)面作者可以選擇他們自己的時(shí)間戳格式。
上一期的 JSP 最佳實(shí)踐介紹了在JSP頁(yè)面中使用自定義標記庫的基礎知識。在那一期可以學(xué)到如何定義簡(jiǎn)單的標記并通過(guò)標記庫描述符(tag library descriptor,TLD)讓其他 JSP 作者可以使用該標記。在這個(gè)星期,我們將以您已經(jīng)知道的有關(guān)自定義標記的知識為基礎。我們上次采用的實(shí)例標記非常簡(jiǎn)單,還存在一些不足之處。因此,現在我們將通過(guò)自定義屬性來(lái)擴展該標記的功能性。
關(guān)于該實(shí)例的一點(diǎn)注解:本期的所有實(shí)例代碼都建立在
上次我們開(kāi)發(fā)的 lastModified 標記的基礎之上。如果您還沒(méi)有完整閱讀上一期,那么在繼續閱讀本期之前您應該回頭閱讀一下上一期。
JSP 標記最常見(jiàn)的需求就是能夠接受來(lái)自某個(gè)頁(yè)面(或者頁(yè)面作者)的數據,并響應該數據。標記 attributes 允許我們將這種功能性融入到自定義標記中。
舉個(gè)非常簡(jiǎn)單的例子,比如典型的“Hello, world”應用程序。很容易想象實(shí)現這一 scriptlet 的功能性的自定義標記是什么樣子,但是,對這個(gè)標記作一點(diǎn)點(diǎn)擴展如何?
在清單 1 中我們可以看到一個(gè) JSP 頁(yè)面片段,其中有一個(gè)典型的“Hello, world!”標記,但是這個(gè)標記包括了一個(gè)名為 name 的屬性。
<p> <examples:hello name="Reader" /></p>
name 屬性為頁(yè)面作者將數據提供給 hello 標記創(chuàng )造了空間,在這個(gè)例子中,所提供的數據是一個(gè)人的名稱(chēng),應用程序將把它的消息轉播給這個(gè)人。實(shí)際上,我們已經(jīng)自定義了“Hello, world” ―― 但我們是怎樣進(jìn)行定義的呢?清單 2 顯示了用于實(shí)現 hello 標記的 Java 代碼 :
package com.ibm.examples;import java.io.IOException;import javax.servlet.jsp.*;import javax.servlet.jsp.tagext.*;public class HelloTag extends TagSupport { // The "person" to say hello to private String name; // Accept the attribute data public void setName(String name) { this.name = name; } public int doEndTag() { try { StringBuffer message = new StringBuffer("Hello, "); message.append(name) .append("!"); pageContext.getOut().println(message.toString()); } catch (IOException ignored) { } return EVAL_PAGE; }
清單 2 中的代碼對于它所做的事情非常簡(jiǎn)單。我們只是添加了一個(gè) setXXX() 方法,其中 XXX 指的是該屬性的名稱(chēng),但正是由于這一添加,我們便已大大擴展了 hello 標記的功能性?,F在,頁(yè)面作者可以為特定用途設置自定義數據,該數據可以根據需要進(jìn)行保存、操縱或者激活。 doEndTag() 方法允許我們以任何方式使用標記數據。
現在,讓討論更深入一點(diǎn),看看當我們添加一個(gè)屬性到 lastModified 標記時(shí)會(huì )發(fā)生什么情況。
我們希望頁(yè)面作者能夠設置他們想要的輸出格式(最后修改數據),而不是只有一種顯示選擇。我們將從后端開(kāi)始,首先擴展 lastModifiedTag 類(lèi), 使其包括一個(gè) java.text.SimpleDateFormat 以用于輸出格式化,如清單 3 所示:
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 { private String format = "MMM d, yyyy"; public int doEndTag() { try { HttpServletRequest request = (HttpServletRequest)pageContext.getRequest(); String path = pageContext.getServletContext().getRealPath( request.getServletPath()); File file = new File(path); <span class="boldcode">DateFormat formatter = new SimpleDateFormat(format);</span> pageContext.getOut().println( formatter.format(new Date(file.lastModified()))); } catch (IOException ignored) { } return EVAL_PAGE; }}
在清單 4 中,我們將新的格式化功能添加到 lastModified 標記中,這是通過(guò)添加一個(gè)屬性辦到的。注意, format 屬性的值與
清單 3 中新引入的 format 方法變量是聯(lián)系在一起的。
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 { private String format = "MMM d, yyyy"; public void setFormat(String format) { this.format = format; } public int doEndTag() { try { HttpServletRequest request = (HttpServletRequest)pageContext.getRequest(); String path = pageContext.getServletContext().getRealPath( request.getServletPath()); File file = new File(path); DateFormat formatter = new SimpleDateFormat(format); pageContext.getOut().println( formatter.format(new Date(file.lastModified()))); } catch (IOException ignored) { } return EVAL_PAGE; }}
format 屬性允許頁(yè)面作者按照自己的喜好設置日期/時(shí)間的輸出格式。不過(guò),在使用這個(gè)新屬性之前,我們必須在 TLD 文件(這個(gè)文件應該是 WEB-INF/tlds 目錄下的 site-utils.tld)中作一點(diǎn)修改,如清單 5 所示:
<?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> <span class="boldcode"><attribute> <name>format</name> </attribute></span> </tag></taglib>
更新了 TLD 之后,我們應該可以開(kāi)始使用新屬性了,那么讓我們來(lái)測試一下吧。重新啟動(dòng) servlet 容器,以確保 servlet 容器接受了新的標記和 TLD 更改,然后連上帶有 lastModified 標記的頁(yè)面。
千真萬(wàn)確,時(shí)間戳出現了。但是我們可以看到,輸出的格式與之前沒(méi)什么兩樣。問(wèn)題就在于沒(méi)有添加新的 format 值,因此我們所看到的格式與老格式是一樣的。這個(gè)小測試展示了為 format 屬性添加默認值(在這個(gè)例子中,就是您馬上要看到的那個(gè)格式)的重要性。
為自定義屬性添加一個(gè)默認值是一種很好的思想,因為這使得頁(yè)面作者在不想提供他們自己的值時(shí)可以省點(diǎn)力氣。在某些情況下,頁(yè)面作者可能會(huì )很高興使用默認值;在其他一些情況下,他們可能需要花點(diǎn)時(shí)間來(lái)了解新的屬性和格式,在這段時(shí)間內,暫時(shí)使用默認值是很有必要的。不管是哪種情況,通過(guò)為標記添加自定義屬性來(lái)創(chuàng )建選項,并為執行某種功能的頁(yè)面提供默認的行為,這樣做才是良好的編程習慣。
當然,我們做這么多事情并不是漫無(wú)目的的!清單 6 顯示了 footer.jsp ,這個(gè)文件來(lái)自原有的那個(gè)時(shí)間戳實(shí)例(參見(jiàn) “
The power of time stamps” ),不過(guò)這里為 format 屬性提供了一個(gè)值:
<%@ 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 <span class="boldcode">format="HH:mm a zz :: MM/dd/yyyy"/></span> </div></td> <td align="left" valign="top"> </td> <td width="141" align="right" valign="top" bgcolor="#330066"> </td> </tr></table><!-- End footer section -->
雖然我們完成了一個(gè)良好的開(kāi)端,但我們還只是簡(jiǎn)要地描繪了關(guān)于處理自定義標記中的屬性的一些表面的東西。在下一期的 JSP 最佳實(shí)踐中,我們將討論一些更復雜的場(chǎng)景,這些場(chǎng)景都是您在創(chuàng )建自定義的、可擴展的時(shí)間戳標記時(shí)可能遇到的――例如錯誤處理和對不同時(shí)區的處理。同時(shí),還將看到為不同類(lèi)型的功能使用自定義屬性的實(shí)踐,到時(shí)候網(wǎng)上見(jiàn)。
您可以參閱本文在 developerWorks 全球站點(diǎn)上的
英文原文.
還是JSP技術(shù)的初學(xué)者嗎?那么請參見(jiàn)由 Brett McLaughlin 撰寫(xiě)的 JSP最佳實(shí)踐系列
前面的幾期。
您或許還想研究一下對遵從JSP規范的IDE的使用。那么下面有一些選擇:
JEditNetBeansEclipseNoel Bergman 的 “
JSP taglibs:設計帶來(lái)更好的可用性”( developerWorks, 2001年12月)介紹了自定義標記庫工具,這種工具允許創(chuàng )建JSTL。
Jeff Wilson 的 “
利用自定義標記控制您的 JSP 頁(yè)面”( developerWorks, 2002年1月)是關(guān)于自定義標記庫通信的入門(mén)讀物。
“
使用 JSP 和 JDBC 技術(shù)的動(dòng)態(tài)的基于 Web 的數據訪(fǎng)問(wèn)”(developerWorks, 2001年9月) 演示了對于訪(fǎng)問(wèn)和操縱JSP頁(yè)面中內容必不可少的一些技術(shù)。
Mark Kolb的 “
JSTL 初級讀物” (developerWorks, 2003年2月至5月)對 JSP 標準標記庫(JSP Standard Tag Library)作了完整的介紹。
要獲得對JSP技術(shù)的指導性的介紹,請參見(jiàn) Noel Bergman 的教程“
JaveServer Pages 技術(shù)簡(jiǎn)介”(developerWorks, 2001年8月)。
要獲得JSP技術(shù)的細節,您最好的選擇是
閱讀 JSP 規范說(shuō)明書(shū)。
Hans Bergsten 的
JavaServer Pages (O‘Reilly & Associates, 2002)是學(xué)習 JSP 技術(shù)時(shí)不可或缺的資料。
參見(jiàn)
developerWorks Java 技術(shù)教程頁(yè)面 以獲得完整的免費教程清單。
在
developerWorks Java 技術(shù)專(zhuān)區 您可以找到大量關(guān)于 Java 編程各個(gè)方面的文章(包括更多關(guān)于JSP技術(shù)的文章)。
關(guān)于作者
Brett McLaughlin 自 Logo 時(shí)期(還記得那個(gè)小三角形嗎?)起就從事計算機方面的工作。目前,他專(zhuān)攻使用 Java 和 Java 相關(guān)的技術(shù)構建應用基礎設施。最近幾年,他一直在 Nextel Communications and Allegiance Telecom 公司,致力于實(shí)現這些基礎設施。Brett 是 Java Apache 項目 Turbine 的合創(chuàng )者之一,Turbine 為使用 Java servlets 的 Web 應用的開(kāi)發(fā)構建了可重用組件基礎設施。對于EJBoss項目(一種開(kāi)源 EJB 應用服務(wù)器)和 Cocoon(一種開(kāi)源 XML Web 發(fā)布引擎),他也作出了貢獻。您可以通過(guò)
brett@oreilly.com 與 Contact Brett 聯(lián)系。