JSP自定义标签
1自定义标签功能
标签:标签是一个可重复使用的单元,用于执行程序中的重复性任务. 例如html中的< h1>,< div>.
自定义标签:指的是用户定义的标签,说得专业点.它是一个可重复使用的组件,并封装了java代码.是用户定义的JSP语言元素.举个例子:用户定义一个< welcome>标签,作用是显示欢迎消息.
这个自定义标签可以在对应项目的JSP页面中使用.
自定义标签功能可通过创建可重用组件来减少JSP中复杂、重复的业务逻辑代码。这些组件可用于其他应用程序。javax.servlet.jsp.tagext 包定义了开发自定义标签 的类和接口。可使用此包的类和接口创建标签处理程序,这些程序可实现带属性的自定义标签、带主体的自定义标签、嵌套自定义标签。
引例:
-
导入jsp.api
-
定义< welcome>标签,WelcomeTag.java。
public class WelcomeTag extends TagSupport { public int doStartTag() throws JspException{ try{ JspWriter out= pageContext.getOut(); out.print("Welcome to custom tags"); } catch (Exception e){ e.printStackTrace(); } //skip_body隐含零,不输出开始标签和结束标签之间的内容 return SKIP_BODY; } }
-
创建tld文件,将WelcomeTag.java封装成一个标签.(tld文件,配置文件的一种,eclipse创建xml文件时修改后缀为tld,idea可以点击创建xml文件类型后选择JSP Tag文件直接创建)
-
welcome.tld
<tag> <!-- 标签名--> <name>welcome</name> <!-- 定义标签的类--> <tag-class>Tag.WelcomeTag</tag-class> <!-- 标签体为空--> <body-content>empty</body-content> </tag>
-
使用自定义的< welcome>标签,welcome.JSP.prefix为定义taglib的名字,也就是调用自定义标签时的前缀。uri为标签库的唯一标识。
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="t" uri="/WEB-INF/welcome.tld"%> <html> <head> <title>Title</title> </head> <body> <t:welcome/> </body> </html>
-
输出
1.1自定义标记API
自定义标签 API 由 javax.servlet.jsp.tagext 包的类和接口组成.javax.servlet.jsp.tagext 包中定义的接口有:
- Tag:定义在标签的生命周期中,可以由 JSP 实现类调用的方法。标签处理程序实现这些方法以执行自定义操作。Tag接口中定义的方法有doEndTag()、doStartTag()、release()、 setPageContext()、setParent()、getParent()。
- IterationTag:扩展了 Tag 接口,除 Tag 接口中定义的方法外,还定义了 doAfterBody() 方法。此方法可重新计算自定义标签的主体内容。
- BodyTag:扩展了 IterationTag接口,定义了可让标签处理程序操作自定义标签主体内容的方法。此接口定义了 doInitBody() 和 setBodyContent() 方法。doInitBody()方法可让标签处理程序准备用于计算的标签。setBodyContent() 方法可让标签处理程序操作标签的主体内容。
javax.servlet.jsp.tagtext 包的各种类:
类 | 描述 |
---|---|
BodyContent | JSPWriter 类的子类,表示标签的主体内容。 |
TagSupport | 作为标签处理程序的基类并实现空标签。 |
BodyTagSupport | 实现 jBodyTag 接口。此类用于开发带主体的自定义标签。 |
TagData | 表示属性及其值。 |
TagInfo | 表示 TLD 文件的元素中指定的信息。JSP 将 JSP页面转换为 servlet 时使用此类。 |
TagLibraryInfo | 表示 TLD 文件的信息,例如它定义的标签和版本信息。 |
TagVariableInfo | 表示自定义标签的变量的相关信息。 |
下表描述了可在标签处理程序中实现的各种方法:
方法 | 描述 |
---|---|
public int doStartTag() | 是由 Tag 接口定义的。当遇到自定义标签的开始标签时调 用此方法。doStartTag() 方法返回 SKIP_BODY 值指定跳过主体内容的处理。此方法还可返回 EVAL_BODY_INCLUDE 值指定应处理标签的主体内容。举个例子,welcome标签为自定义标签,< welcome>欢迎</ welcome>,如果返回值是SKIP_BODY,则"欢迎"这个中间体不会输出,返回值是 EVAL_BODY_INCLUDE ,则会输出. |
public void release() | 是由 Tag 接口定义的。调用此方法可让标签处理程序释放其部分资源。 |
doAfterBody() | 是由 BodyTagSupport 类实现的。计算主体标签后调用此方法。doStartTag() 方法返回 EVAL_BODY_AGAIN 值指定应重新计算主体内容。doStartTag() 方法返回 SKIP_BODY 值指定跳过主体内容的计算。 |
public int doEndTag() | 是由 Tag 接口定义的。当遇到自定义标签的结束标签时调用此方法。doEndTag() 方法返回 EVAL_PAGE 值以处理剩余的 JSP 页面或返回 SKIP_PAGE 值以跳过剩余页面的处理。 |
javax.servlet.jsp 包包含 PageContext 类,该类表示 JSP 页面并将 JSP 页面的信息提供给 标签处理程序。PageContext 类定义可在标签处理程序中调用来访问 JSP 页面各种隐式对象的方法。下表描述了 PageContext 类的一些重要方法:
方法 | 描述 |
---|---|
public abstract JspWriter getOut() | 返回 JSP 隐式对象 out。 |
public abstract ServletRequest getRequest() | 返回 JSP 隐式对象 request。 |
public abstract ServletResponse getResponse() | 返回 JSP 隐式对象 response。 |
public abstract HttpSession getSession() | 返回 JSP 隐式对象 session。 |
public abstract Exception getException() | 返回 JSP 隐式对象 exception。 |
public abstract ServletContext getServletContext() | 返回 JSP 隐式对象 application。 |
public abstract ServletConfig getServletConfig() | 返回 JSP 隐式对象 config。 |
public abstract Object getPage() | 返回 JSP 隐式对象 page。 |
1.2自定义标签
自定义标签的类型有
- 带属性的自定义标签
- 带主体的自定义标签
- 嵌套自定义标签
1.2.1带属性的自定义标签
html中img标签有src属性等,而用户自定义标签也可以带有属性值。可使用各种属性定制自定义标签执行的功能。每个自定义属性 (attribute) 需定义一个属性 (property)。可使用 getXXX() 和 setXXX() 方法访问和设置标签 处理程序类中的属性。
举个例子,自定义< goods>标签
<t:goods type='books'></t:goods>
要实现此标签,需在标签处理程序类中定义属性 (property) copyright。还需定义 setCopyright() 和 getCopyright() 方法分别设置和访问该属性。可用以下代码段在标签处理程序中声明属性:
public class Goods extends TagSupport {
String type;
//实现特定功能
//获取Type值
public String getType(){
return this.type;
}
//set type value
public void setType(String type){
this.type=type;
}
}
要在 TLD 文件中定义带属性的自定义标签,需指定自定义标签包含 TLD 文件的属性元素中的属性。 该属性元素包含以下子元素:
- name:指定属性名称
- required:指定属性是必需的还是可选的。值 true 表示 JSP 页面中必须有此属性值。
- rtexprvalue:指定运行时表达式(例如 JSP 表达式)是否可用于此属性。可能的值有 true、false、yes、no。
例如上面的goods标签,想要用自定义标签的属性,需要在tld文件配置
<tag>
<name>Goods</name>
<tag-class>Tag.Goods</tag-class>
<body-content>empty</body-content>
<attribute>
<name>type</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
以上代码配置了Goods用户自定义标签,同时配置了属性值type。required值为true意味着在使用Goods标签时type属性必须使用。
1.2.2带主体的自定义标签
主体标签中括住文本,js,JSP表达式等其它jsp组件的自定义标签称为带主体的自定义标签。
举个例子
< t:welcome>hello </ t:welcome>
开始标签和结束标签中有文本,带主体的自定义标签之一.
在tld文件中配置标签时,可以指定自定义标签是否包含主体,当包含主体时应该在< tag>中使用下面语句.
<body-content>JSP</body-content>
不包含主体时,< body-content>内容为empty.表示容器将不会计算主体内容.例如上面welcome标签例子中,hello将不会被处理输出到页面上.
<body-content>empty</body-content>
具体配置例子为
<tag>
<name>HelloTag</name>
<tag-class>hello.HelloTagHandler</tag-class>
<body-content>JSP</body-content>
</tag>
带主体的自定义标签中,主体可以通过调用其 doAfterBody() 方法中 BodyTagSupport 类 的 getBodyContent() 方法获取.这个方法将返回bodycontent对象.调用其 doAfterBody() 方法中 BodyTagSupport 类 的 getBodyContent() 方法获取具体主体内容.
下面是获取主体内容后并打印输出的例子
public int doAfterBody() throws JspException
{
try
{
/* 获取 BodyContent的实例 */
BodyContent bc = getBodyContent();
//获取主题字符串内容
String body = bc.getString();
JspWriter out = bc.getEnclosingWriter();
out.println(body.toUpperCase());
......
1.2.3嵌套的自定义标签
嵌套标签,举个html例子方便理解
<div><h1>hello</h1></div>
这就是一个嵌套标签,h1标签嵌套在div标签内.一个标签的主体部分含有其它标签,即为嵌套标签.外围标签为父标签.里面标签为子标签.
子标签使用 Tag 接口的 getParent() 方法 或 TagSupport 类的 findAncestorWithClass() 方法与父标签进行通信。
嵌套的自定义标签例子,子标签返回父标签属性值,如果为true则输出主体内容.
嵌套标签
<pdtd:ParentTag condition= “true” >
<chtd:ChildTag>
The expression evaluates to true
</chtd:ChildTag>
</pdtd:ParentTag>
父标签代码
import javax.servlet.jsp.tagext.*;
import class ParentTag extends TagSupport
{
private boolean condition;
public int doStartTag()
{
return EVAL_BODY_INCLUDE;
}
public void setCondition (Boolean condition)
{
this.condition = condition;
}
public boolean getCondition()
{
return condition;
}
}
在上述代码中,标签接受属性 condition。doStartTag() 方法返回 EVAL_BODY_INCLUDE,它指定要计算的标签主体。
子标签代码
import javax.servlet.jsp.tagext.*;
import class ChildTag extends TagSupport
{
public int doStartTag()
{
ParentTag parent = (ParentTag) getParent();
/* 使用getparent()方法获取父标签 */
boolean condition = parent.getCondition();
/* 使用getCondition()方法获取父标签的condition属性 */
if(condition)
{
return EVAL_BODY_INCLUDE;
}
else
{
return SKIP_BODY;
}
}/* End doStartTag() */
}
condition为true时返回EVAL_BODY_INCLUDE,为flase时SKIP_BODY.