概述 | <nobr>第 1 页(共4 页)</nobr> |
有时,可能希望根据一些特定于应用程序的显示逻辑有条件地调用方法的正文。可以通过从 doStartTag()
返回一个值做到这一点:SKIP_BODY
跳过标签的正文,而 EVAL_BODY
对它进行判断。
迭代标签需要实现 IterationTag
接口。容器调用 doAfterBody()
方法以确定是否要重新判断正文。这个方法返回 EVAL_BODY_AGAIN
时表明容器应当继续对正文进行判断。doAfterBody()
方法返回 SKIP_BODY
时表明迭代操作已经结束。TagSupport 类和 BodyTagSupport
类都实现了 IterationTag
接口。
回到 map 主题,在下面几小节我们将创建一个自定义标签处理程序,它迭代一组 map 并打印出它们的值。
控制流程序示例 | <nobr>第 2 页(共4 页)</nobr> |
在开始实现自定义标签处理程序之前,让我们看看示例标签是如何使用的。首先,用上面的例子中的 map
标签定义两个 map:
<%@taglib uri="map" prefix="map"%> <map:map id="employee1"> { firstName=Jennifer, lastName=Wirth, age=33 } </map:map> <map:map id="employee2"> { firstName=Kiley, lastName=McKeon, age=27 } </map:map>
然后,创建一个名为 list
的集合,加入上面定义的两个 map (employee1
和 employee2
)。
<jsp:usebean id="list" class="java.util.ArrayList"></jsp:usebean> <% list.add(employee1); list.add(employee2); %>
下面展示了如何用自定义标签迭代这个集合:
<map:printmaps name="list"> </map:printmaps>
First Name
${firstName}
Last Name
${lastName}
Age
${age}
map:printMaps
标签迭代 list
集合。 在每一次迭代时,它使用其正文,通过搜索以 ${key}
开始的子字符,在 map 中搜索当前迭代的键。它从字符串 ${}
中解析出键,并用从 map 得到的这个键的值(即 map.get(key)
)替换它。
实现 doStartTag() 方法 | <nobr>第 3 页(共4 页)</nobr> |
doStartTag()
方法从范围中抓取集合,然后从集合中抓取迭代器。 如果迭代器中没有任何项(iter.hasNext()
),那么 doStartTag()
方法就返回 SKIP_BODY
,从而实现了逻辑 if
。在这种情况下,这个 if
等同于“如果集合为空,则跳过正文判断”。 然后迭代器从集合中抓取第一个 map。
public class MapPrintMapsTag extends BodyTagSupport { private String name; private Iterator iter; private Map map; private String scope; public int doStartTag() throws JspException { Collection collection = null; /* Grab the collection out of scope using the scope attribute. */ if (scope == null){ collection = (Collection) pageContext.findAttribute(name); }else if("page".equalsIgnoreCase(scope)){ collection = (Collection) pageContext.getAttribute(name); }else if("request".equalsIgnoreCase(scope)){ collection = (Collection) pageContext.getRequest().getAttribute(name); }else if("session".equalsIgnoreCase(scope)){ collection = (Collection) pageContext.getSession().getAttribute(name); }else if("application".equalsIgnoreCase(scope)){ collection = (Collection) pageContext.getServletContext().getAttribute(name); } /* Get the iterator from the collection. */ iter = collection.iterator(); /* If the collection is empty skip the body evaluation. */ if (iter.hasNext()==false) return SKIP_BODY; /* Grab the first map out of the collection. */ map = (Map)iter.next(); return EVAL_BODY_BUFFERED; }
实现 doAfterBody() 方法 | <nobr>第 4 页(共4 页)</nobr> |
doAfterBody()
通过在没有可迭代的项时返回 SKIP_BODY
实现了迭代。如果还有项,那么它就返回 EVAL_BODY_AGAIN
。只要 doAfterBody()
返回 EVAL_BODY_AGAIN
,容器就会继续对正文进行判断,如下所示:
public int doAfterBody() throws JspException { /** Process body */ ... /** Write the processed buffer to the previous out */ ... if (iter.hasNext() == false) { return SKIP_BODY; } else { map = (Map) iter.next(); return EVAL_BODY_AGAIN; } }
doAfterBody()
方法用 getBodyContent()
抓取正文,然后用正文内容的 getString()
方法得到字符串形式的内容。 下一步它清除正文内容缓冲区,用 StringTokenizer
查找以 ${
开始的字符串。 它还创建一个名为 buffer
的 StringBuffer
。 当它迭代 tokenizer 中的字符串时,将它们附加到缓冲区中。
如果 tokenizer 中的字符串以 ${
, doAfterBody()
开始,那么它就从字符串中提取键,并用这个键在 map 中查找值。它调用值对象的 toString()
方法将 map 中的值转换为字符串,并将结果附加到缓冲区中。下面的清单展示了这个过程是如何进行的。
/** Process body */ /* Get and clear the body */ BodyContent body = this.getBodyContent(); String content = body.getString(); body.clearBody(); /* Process the body with a String tokenizer */ StringTokenizer token = new StringTokenizer(content); /* Create an output buffer of the processed body */ StringBuffer buffer = new StringBuffer(content.length() * 2); /* Iterate over the strings from the tokenizer and put them into the output buffer. */ while (token.hasMoreTokens()) { String tok = token.nextToken(); /* See if the String contains the special substring "${" */ if (tok.startsWith("${")) { /* Parse the key out of the string */ String key = tok.substring(2, tok.length() - 1); /* Use the key to look up the object in the map */ Object value = (String) map.get(key); String svalue = tok; /* If the value is not null, get the value's string representation */ if (value != null) { svalue = value.toString(); } /* Add the string representation of the value to the output buffer */ buffer.append(svalue + " "); } else { buffer.append(tok + " "); } }
doAfterBody()
方法在构建了输出缓冲区后,就将它输出到前面的 out
中,如下所示:
try { this.getPreviousOut().print(buffer.toString()); } catch (IOException e) { throw new JspException(e); }
这样,每一次迭代都处理正文并将它输出到前面的 out
中。如果前面的 out
是根 JspWriter
,那么它就会写到浏览器中。
结束语 | <nobr>第 1 页(共3 页)</nobr> |
学 完本教程,您对自定义标签有了更深入的理解。如果您学完了本教程,即便是刚接触自定义标签,也应当可以开始在正在开发的项目中使用这种强大的工具了。您将 再也无法忍受在 JSP 页散布的那些难以维护和和不可重用的普通 Java scriptlet 了。即使不准备创建自已的自定义标签,本教程也会帮助您理解在由其他开发人员编写的代码中遇到的自定义标签。
参考资料 | <nobr>第 2 页(共3 页)</nobr> |