上一期的 JSP 最佳实践介绍了在JSP页面中使用自定义标记库的基础知识。在那一期可以学到如何定义简单的标记并通过标记库描述符(tag library descriptor,TLD)让其他 JSP 作者可以使用该标记。在这个星期,我们将以您已经知道的有关自定义标记的知识为基础。我们上次采用的实例标记非常简单,还存在一些不足之处。因此,现在我们将通过自定义属性来扩展该标记的功能性。
关于该实例的一点注解:本期的所有实例代码都建立在 上次我们开发的 lastModified
标记的基础之上。如果您还没有完整阅读上一期,那么在继续阅读本期之前您应该回头阅读一下上一期。
JSP 标记最常见的需求就是能够接受来自某个页面(或者页面作者)的数据,并响应该数据。标记 attributes 允许我们将这种功能性融入到自定义标记中。
举个非常简单的例子,比如典型的“Hello, world”应用程序。很容易想象实现这一 scriptlet 的功能性的自定义标记是什么样子,但是,对这个标记作一点点扩展如何?
在清单 1 中我们可以看到一个 JSP 页面片段,其中有一个典型的“Hello, world!”标记,但是这个标记包括了一个名为 name
的属性。
清单 1. 一个简单的 "Hello, world!" 标记
<p> <examples:hello name="Reader" /> </p> |
name
属性为页面作者将数据提供给 hello
标记创造了空间,在这个例子中,所提供的数据是一个人的名称,应用程序将把它的消息转播给这个人。实际上,我们已经自定义了“Hello, world” ―― 但我们是怎样进行定义的呢?清单 2 显示了用于实现 hello
标记的 Java 代码 :
清单 2. hello 标记的代码
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 中的代码对于它所做的事情非常简单。我们只是添加了一个 setXXX()
方法,其中 XXX
指的是该属性的名称,但正是由于这一添加,我们便已大大扩展了 hello
标记的功能性。现在,页面作者可以为特定用途设置自定义数据,该数据可以根据需要进行保存、操纵或者激活。 doEndTag()
方法允许我们以任何方式使用标记数据。
现在,让讨论更深入一点,看看当我们添加一个属性到 lastModified
标记时会发生什么情况。
|
我们希望页面作者能够设置他们想要的输出格式(最后修改数据),而不是只有一种显示选择。我们将从后端开始,首先扩展 lastModifiedTag
类, 使其包括一个 java.text.SimpleDateFormat
以用于输出格式化,如清单 3 所示:
清单 3. 在 LastModifiedTag 类中使用 SimpleDateFormat
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 标记中,这是通过添加一个属性办到的。注意, format
属性的值与 清单 3 中新引入的 format
方法变量是联系在一起的。
清单 4. 处理新属性
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
属性允许页面作者按照自己的喜好设置日期/时间的输出格式。不过,在使用这个新属性之前,我们必须在 TLD 文件(这个文件应该是 WEB-INF/tlds 目录下的 site-utils.tld)中作一点修改,如清单 5 所示:
清单 5. 修改 TLD
<?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 之后,我们应该可以开始使用新属性了,那么让我们来测试一下吧。重新启动 servlet 容器,以确保 servlet 容器接受了新的标记和 TLD 更改,然后连上带有 lastModified
标记的页面。
千真万确,时间戳出现了。但是我们可以看到,输出的格式与之前没什么两样。问题就在于没有添加新的 format
值,因此我们所看到的格式与老格式是一样的。这个小测试展示了为 format
属性添加默认值(在这个例子中,就是您马上要看到的那个格式)的重要性。
为自定义属性添加一个默认值是一种很好的思想,因为这使得页面作者在不想提供他们自己的值时可以省点力气。在某些情况下,页面作者可能会很高兴使用默认值;在其他一些情况下,他们可能需要花点时间来了解新的属性和格式,在这段时间内,暂时使用默认值是很有必要的。不管是哪种情况,通过为标记添加自定义属性来创建选项,并为执行某种功能的页面提供默认的行为,这样做才是良好的编程习惯。
|
当然,我们做这么多事情并不是漫无目的的!清单 6 显示了 footer.jsp ,这个文件来自原有的那个时间戳实例(参见 “ The power of time stamps” ),不过这里为 format
属性提供了一个值:
清单 6. 使用 format 属性
<%@ 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 --> |
虽然我们完成了一个良好的开端,但我们还只是简要地描绘了关于处理自定义标记中的属性的一些表面的东西。在下一期的 JSP 最佳实践中,我们将讨论一些更复杂的场景,这些场景都是您在创建自定义的、可扩展的时间戳标记时可能遇到的――例如错误处理和对不同时区的处理。同时,还将看到为不同类型的功能使用自定义属性的实践,到时候网上见。