14.1 自定义标签介绍
JSP标签库(也称自定义库)可看成是一套产生基于XML脚本的方法,它经由JavaBeans来支持。在概念上说,标签库是非常简单和可以重用的代码构造。 自定义标签有着丰富的特点,它们可以:
下面的代码声明在网页中使用新的标签,这个标签以hello为前缀,访问的路径为/demotag。 <%@ taglib uri="/demotag" prefix="hello" %> 自定义标签为在JSP项目中创建易于重用的代码打开了一扇大门。你所需要的只是标签库和它的文档说明。 通过实现接口或者继承现有的类,我们就可以开发自定义的标签。 TagSupport类 该类是IterationTag的缺省实现。除了实现原有方法外,本身还增加了一些有用的其他方法和成员变量。下表列出其中重要的几个:
BodyTagSupport类 该类同时继承了TagSupport类,并实现BodyTag接口。除了前表所示方法,该类还提供了一些其它方法便于使用。
实现Tag接口 所有的标签处理器都需要间接或直接的实现这个接口。 下面列出Tag接口定义的方法和常量:
setPageContext()方法 setPageContext()方法是一个定制标签声明周期内第一个要被调用的方法。完整写法是: public void setPageContext(PageContext); jsp引擎会将jsp页转换时隐含创建的pageContext对象,作为参数,调用setPageContext方法。 通常的做法会将这个参数保存为本标记处理器的参数。 setParent()和getParent()方法 setter方法 doStartTag()方法 doEndTag()方法 release()方法 自定义标签的开发包括两个部分的开发: 自定义标签的种类有许多,可以根据实际项目的需要进行编写。但为了不重复的开发,jsp标准推出JSTL(标准标签库)。 在JSP应用程序中添加自定义标签的能力可以使工作重点放到以文档为中心的开发方式上。可以使 Java 代码不出现在 JSP 页中,从而使这些页面更容易维护。 在创建自定义标签之前,需要创建一个 标签处理程序。标签处理程序是一个执行自定义标签操作的 Java 对象(Java类)。在使用自定义标签时,要导入一个 标签库 —— 即一组标签/标签处理程序对,这样页面编译时才能处理你的自定义标签。通过在 Web 部署描述符(web.xml)中声明库导入它,然后用指令taglib将它导入 JSP 页。 如果JSP容器在编译JSP文件时遇到了自定义标签,那么它就检查 标签库描述符(tag library descriptor)(TLD) 文件以查询相应的标签处理程序。TLD 文件对于自定义标签处理程序,就像 Web 部署描述符对于 servlet 一样。 对正文进行操作 —— 即对在开始和结束标签之间的内容进行操作的 —— 标签必须实现 BodyTag 接口。我们将称这些标签为正文标签。我们将不对其正文操作的标签称为简单标签。简单标签可以实现Tag接口,尽管不要求它们这样做。要记住不对其正文操作的标签仍然有正文,只不过,它的标签处理程序不能读取这个正文。 1. 创建实现了Tag接口(准确地说是 javax.servlet.jsp.tagext.Tag)的标签处理程序类。 14.2 HelloWorld标签的开发 上一节 下一节 本章开头 传统的标签必须实现javax.servlet.jsp.tagext.Tag接口,在Tag接口中,主要定义的是和标签生命周期相关的方法,比如:doStartTag(),doEndTag()等。在Tag中,可以通过PageContext对象来访问JSP页面的上下文。结合标签的生命周期标签的处理过程: 2使用setParent方法设置这个标签的上一级标签,如果没有上一级嵌套,设置为null。 3设置标签的属性,这个属性在标签库描述文件中定义,如果没有定义属性,就不用调用此类方法。 4调用doStartTag方法,这个方法可以返回EVAL_BODY_INCLUDE和SKIP_BODY,当返回EVAL_BODY_INCLUDE时,就计算标签的Body,如果返回SKIP_BODY,就不计算标签的Body。 5调用doEndTag方法,这个方法可以返回EVAL_PAGE或者SKIP_PAGE,当返回EVAL_PAGE时,容器将在标签结束时继续计算JSP页面其他的部分;如果返回SKIP_PAGE,容器将在标签结束时停止计算JSP页面其他的部分。 6调用release方法释放标签程序占用的任何资源。 开发标签时可以有两种选择,已从是实现原始的接口,另一种是从TagSupport类继承。 14.2.1 实现Tag接口 按照下面的步骤进行: 1)开发标签实现类 2)编写标签描述文件,tld为扩展名。 3)在Web.xml中映射标签库的使用。 4)在JSP网页中调用标签。 14.2.1.1开发实现类 教材例程14-1,HelloTag_Interface.java文件。 package com.jspdev.ch14;
import javax.servlet.jsp.*; import javax.servlet.jsp.tagext.*; import java.util.Hashtable; import java.io.Writer; import java.io.IOException; import java.util.Date;
/** *演示怎么实现Tag接口的方式来开发标签程序 */ public class HelloTag_Interface implements javax.servlet.jsp.tagext.Tag { private PageContext pageContext; private Tag parent; public HelloTag_Interface() { super(); }
/** *设置标签的页面的上下文 */ public void setPageContext(final javax.servlet.jsp.PageContext pageContext) { this.pageContext=pageContext; }
/** *设置上一级标签 */ public void setParent(final javax.servlet.jsp.tagext.Tag parent) { this.parent=parent; }
/** *开始标签时的操作 */ public int doStartTag() throws javax.servlet.jsp.JspTagException { return SKIP_BODY; //返回SKIP_BODY,表示不计算标签体 }
/** *结束标签时的操作 */ public int doEndTag() throws javax.servlet.jsp.JspTagException { try { pageContext.getOut().write("Hello World!你好,世界!"); } catch(java.io.IOException e) { throw new JspTagException("IO Error: " + e.getMessage()); } return EVAL_PAGE; }
/** *release用于释放标签程序占用的资源,比如使用了数据库,那么应该关闭这个连接。 */ public void release() {}
public javax.servlet.jsp.tagext.Tag getParent() { return parent; } } 14.2.1.2编写标签库描述 教材例程14-2,mytag.tld文件。 <?xml version="1.0" encoding="ISO-8859-1" ?>
<taglib xmlns="http://java./xml/ns/j2ee" xmlns:xsi="http://www./2001/XMLSchema-instance" xsi:schemaLocation="http://java./xml/ns/j2ee web-jsptaglibrary_2_0.xsd" version="2.0"> <description>A tag library exercising SimpleTag handlers.</description> <tlib-version>1.0</tlib-version>
<short-name>examples</short-name> <uri>/demotag</uri> <description> A simple tab library for the examples </description>
<tag> <description>Outputs Hello, World,从实现Tag接口起开发</description> <name>hello_int</name> <tag-class>com.jspdev.ch14.HelloTag_Interface</tag-class> <body-content>empty</body-content> </tag>
</taglib> 14.2.1.3 在Web.xml中映射标签库的使用。 <web-app> <taglib> ... 14.2.1.4 在JSP网页中调用标签 教材例程14-4,hellotag_interface.jsp文件。 <%@ taglib uri="/demotag" prefix="hello" %> 运行结果如下图所示。 14.2.2 从TagSupport继承 只需要覆盖TagSupport类的doStartTag和doEndTag两个方法即可,开发比较容易。 教材例程14-5,HelloTag.java文件。 package com.jspdev.ch14;
import javax.servlet.jsp.*; import javax.servlet.jsp.tagext.*; import java.util.Hashtable; import java.io.Writer; import java.io.IOException; import java.util.Date;
/** *演示从TagSupport继承来开发标签 */ public class HelloTag extends TagSupport { /** *覆盖doStartTag方法 */ public int doStartTag() throws JspTagException { return EVAL_BODY_INCLUDE; }
/** *覆盖doEndTag方法 */ public int doEndTag()throws JspTagException { String dateString =new Date().toString(); try { pageContext.getOut().write("Hello World hellking.<br>现在的时间是:"+dateString);
} catch(IOException ex) { throw new JspTagException("Fatal error:hello tag conld not write to JSP out"); } return EVAL_PAGE; } } 教材例程14-6,mytag.tld文件,同上例,加一段映射即可。 ...
<tag> <name>hello</name> <tag-class>com.jspdev.ch14.HelloTag</tag-class> <body-content>empty</body-content> <description> Simple hello world examples. Takes no attribute,and simply generates HTML </description> </tag>
... 教材例程14-7,helloworld_tag.jsp文件。 <%@ taglib uri="/demotag" prefix="hello" %> 运行结果如下图所示。
带有Body的Tag必须实现javax.servlet.jsp.tagext.BodyTag接口,BodyTag接口中定义了一些处理标签体的方法。 教材例程14-8,BodyTagExample.java文件。 package com.jspdev.ch14; import javax.servlet.jsp.*; import javax.servlet.jsp.tagext.*; import java.util.Hashtable; import java.io.Writer; import java.io.IOException;
public class BodyTagExample extends BodyTagSupport { int counts;//counts为迭代的次数。 public BodyTagExample() { super(); }
/** *设置counts属性。这个方法由容器自动调用。 */ public void setCounts(int c) { this.counts=c; }
/** *覆盖doStartTag方法 */ public int doStartTag() throws JspTagException { System.out.println("doStartTag"); if(counts>0) { return EVAL_BODY_TAG; } else { return SKIP_BODY; } }
/** *覆盖doAfterBody方法 */ public int doAfterBody() throws JspTagException { System.out.println("doAfterBody"+counts); if(counts>1) { counts--; return EVAL_BODY_TAG; } else { return SKIP_BODY; } }
/** *覆盖doEndTag方法 */ public int doEndTag() throws JspTagException { System.out.println("doEndTag"); try { if(bodyContent != null) { bodyContent.writeOut(bodyContent.getEnclosingWriter()); } } catch(java.io.IOException e) { throw new JspTagException("IO Error: " + e.getMessage()); } return EVAL_PAGE; }
public void doInitBody() throws JspTagException{ System.out.println("doInitBody"); } public void setBodyContent(BodyContent bodyContent) { System.out.println("setBodyContent"); this.bodyContent=bodyContent; } } 教材例程14-8,同mytag.tld文件,加上下述一段即可。 <tag> <name>loop</name> <tag-class>com.jspdev.ch14.BodyTagExample</tag-class> <body-content>jsp</body-content> <attribute> <name>counts</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag> 教材例程14-10,bodytag.jsp文件,调用上述标签。 <%@ taglib uri="/demotag" prefix="bodytag" %> 运行结果如下图所示。 类似于循环的嵌套和开关语句,在实际开发中,往往需要多个标签嵌套在一起完成特定的任务。教材中例举的例子如下: <mt:switch value="test"> 上面的标签中,<mt:switch>为父标签,<mt:case>为子标签,一个父标签可以嵌套多个子标签和HTML、Scriptlets等。 教材例程14-11,IfTag.java文件。 package com.jspdev.ch14; import javax.servlet.jsp.*; import javax.servlet.jsp.tagext.*; import java.util.Hashtable; import java.io.Writer; import java.io.IOException;
/** *if Tag *usage:<tag:if value=true> * ... * </tag:if> */ public class IfTag extends BodyTagSupport { private boolean value; /** *设置属性的值。 */ public void setValue(boolean value) { this.value=value; }
/** *doStartTag方法,如果value为true,那么 *就计算tagbody的值,否则不计算body的值。 */ public int doStartTag() throws JspTagException { if(value) { System.out.println("value is true"); return EVAL_BODY_INCLUDE; } else { System.out.println("value is false"); return SKIP_BODY; } }
/** *覆盖doEndTag方法 */ public int doEndTag() throws JspTagException { try { if(bodyContent != null) { bodyContent.writeOut(bodyContent.getEnclosingWriter()); } } catch(java.io.IOException e) { throw new JspTagException("IO Error: " + e.getMessage()); } return EVAL_PAGE; }
} 教材例程14-12,OutTag.java文件。IfTag中嵌套了一个子标签,子标签的Java文件为OutTag.java。 package com.jspdev.ch14; import javax.servlet.jsp.*; import javax.servlet.jsp.tagext.*; import java.util.Hashtable; import java.io.Writer; import java.io.IOException;
public class OutTag extends TagSupport { private Object value;
/** *覆盖doStartTag方法 */ public void setValue(Object value) { this.value=value; }
public int doStartTag() throws JspTagException { return EVAL_BODY_INCLUDE; }
/** *覆盖doEndTag方法 */ public int doEndTag()throws JspTagException {
try { System.out.println(value); pageContext.getOut().write(value.toString());
} catch(IOException ex) { throw new JspTagException("Fatal error:hello tag conld not write to JSP out"); } return EVAL_PAGE; }
} 教材例程14-13,同mytag.tld文件,加上下述一段即可。 <tag> <name>if</name> <tag-class>com.jspdev.ch14.IfTag</tag-class> <body-content>jsp</body-content> <attribute> <name>value</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag> <tag> <name>out</name> <tag-class>com.jspdev.ch14.OutTag</tag-class> <body-content>jsp</body-content> <attribute> <name>value</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag> 教材例程14-14,cortag.jsp文件,调用上述标签。 <%@ taglib uri="/demotag" prefix="mt" %> 运行结果如下图:
在没有迭代标签的时候,要循环输出内容到网页,一般采用while语句进行循环,如: while(rst.next()) 上述循环结构可以用迭代标签替代。 如果要实现内容的迭代输出,需要开发两个类,一个类实现BodyTagSupport接口,另一个类扩展TagExtraInfo类。TagExtraInfo旨在提供标签运行时的信息。 教材例程14-15,IterateTag.java文件 ,实现BodyTagSupport类的接口 package com.jspdev.ch14; import javax.servlet.jsp.*; import javax.servlet.jsp.tagext.*; import java.util.*;
public class IterateTag extends BodyTagSupport { /** *Tag的属性 */ private String name; //it为要迭代的对象 private Iterator it; //type表示it中对象的类型 private String type;
/** *设置属性collection */ public void setCollection(Collection collection) { if(collection.size()>0) it=collection.iterator(); } public void setName(String name) { this.name=name; } public void setType(String type) { this.type=type; } /** *如果it属性为null,那么忽略计算tagbody。 */ public int doStartTag()throws JspTagException { if(it==null) return SKIP_BODY; else return continueNext(it); }
public int doAfterBody()throws JspTagException { return continueNext(it); }
public int doEndTag()throws JspTagException { try { if(bodyContent!=null) bodyContent.writeOut(bodyContent.getEnclosingWriter()); } catch(java.io.IOException e) { }
return EVAL_PAGE; } /** *保护方法,用于把it.next()设置为pagecontext的属性 */ protected int continueNext(Iterator it)throws JspTagException { if(it.hasNext()) { pageContext.setAttribute(name,it.next(),PageContext.PAGE_SCOPE); return EVAL_BODY_TAG; } else { return SKIP_BODY; } } } 教材例程14-16,IterateTEI.java文件,为表示信息的标签类。 import javax.servlet.jsp.tagext.*; //TagExtraInfo用于提供一些在标签翻译时相关的信息。 public class IterateTEI extends TagExtraInfo { public IterateTEI() { super(); }
public VariableInfo[] getVariableInfo(TagData data) { return new VariableInfo[] { new VariableInfo( data.getAttributeString("name"), data.getAttributeString("type"), true, VariableInfo.NESTED ), }; } } 其中AT_BEGIN, NESTED, AT_END是标签扩展信息类(TEI)的VariableInfo中定义,如果设置为AT_BEGIN,变量在当前动作元素的开始标记之后就对调用者可见。如果此属性设置为AT_END,那么变量则在结束标记之后可见。NESTED 说明它仅在开始和结束标记之间可见。 教材例程14-17,同mytag.tld文件,加上下述一段即可。 <tag> <name>iterate</name> <tag-class>com.jspdev.ch14.IterateTag</tag-class> <tei-class>com.jspdev.ch14.IterateTEI</tei-class> <body-content>jsp</body-content> <attribute> <name>collection</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <name>name</name> <required>true</required> </attribute> <attribute> <name>type</name> <required>true</required> </attribute> </tag> 教材例程14-18,iterator.jsp文件,调用上述标签。 <%@ taglib uri="/demotag" prefix="mt"%> <% com.jspdev.ch14.Company comp=new com.jspdev.ch14.Company(); comp.setName("huayuan"); com.jspdev.ch14.Contact contact1=new com.jspdev.ch14.Contact(); contact1.setName("bar ,foo"); contact1.setEmail("dd@tsinghua.eud.cn"); contact1.setPhone("2975349875"); contact1.setComment("java programe");
com.jspdev.ch14.Contact contact2=new com.jspdev.ch14.Contact(); contact2.setName("bar ,foo"); contact2.setEmail("dd@tsinghua.eud.cn"); contact2.setPhone("2975349875"); contact2.setComment("java programe");
com.jspdev.ch14.Contact contact3=new com.jspdev.ch14.Contact(); contact3.setName("bar ,foodf"); contact3.setEmail("dd@tsinghua.euddfdf.cn"); contact3.setPhone("2975349875dd"); contact3.setComment("java progrdfame");
comp.addContact(contact1); comp.addContact(contact2); comp.addContact(contact3); request.setAttribute("company",comp); %> <html> <head> <title>迭代标签演示</title> </head> <body> <jsp:useBean id="company" scope="request" type="com.jspdev.ch14.Company"/>
<font size=+2>我的一些和<b> <jsp:getProperty name="company" property="name"/> 公司联系</b> </font> <hr> <table border=1> <tr> <td>姓名</td> <td>电话</td> <td>email</td> <td>备注</td> </tr> <mt:iterate name="contact" collection="<%=company.getContacts()%>" type="com.jspdev.ch14.Contact"> <tr> <td> <jsp:getProperty name="contact" property="name"/> </td> <td> <jsp:getProperty name="contact" property="phone"/> </td> <td> <jsp:getProperty name="contact" property="email"/> <td> <jsp:getProperty name="contact" property="comment"/> </td> </tr> </mt:iterate> </table> <hr> </body> </html> 运行结果如下图: SimpleTag有自己的生命周期,最主要就是doTag方法。 1) 每次遇到标签,容器构造一个SimpleTag的实例,这个构造方法没有参数。和红典的标签一样,SimpleTag不能缓冲,故不能重用,每次都需要构造新的实例。 教材例程14-19,HelloWorldSimpleTag.java文件。 package com.jspdev.ch14;
import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.SimpleTagSupport; import java.io.IOException;
public class HelloWorldSimpleTag extends SimpleTagSupport {
public void doTag() throws JspException, IOException { getJspContext().getOut().write( "<table border=1><tr bgcolor=9944cc><td>simpeltag测试</tr></td><tr tr=cc44cc><td>helloWorld!</td></tr></table>" ); } } 教材例程14-20,同mytag.tld文件,加上下述一段即可。 <tag> <description>Outputs Hello, World</description> <name>helloWorld</name> <tag-class>com.jspdev.ch14.HelloWorldSimpleTag</tag-class> <body-content>empty</body-content> </tag> 教材例程14-21,simple_tag.jsp文件,调用上述标签。 <%@ page contentType="text/html; charset=gb2312" language="java" %> <%@ taglib uri="/demotag" prefix="mt"%> <html> <head> <title>JSP 2.0 Examples - 简单的标签</title> </head> <body> <h1>JSP 2.0 Examples - 简单的标签</h1> <hr> <p>这里是一个非常简单的标签.</p> <br> <b><u>Result:</u></b> <mt:helloWorld/> </body> </html>
|
|