3.1、开发自定义标签的步骤
1)编写一个普通的java类,继承SimpleTagSupport类,叫标签处理器类
2)在web项目的WEB-INF目录下建立rk.tld文件,这个.tld文件叫标签库的声明文件。(参考核心标签库的tld文件)
3) 在jsp页面的头部导入自定义标签库:<%@taglib uri="http:///rk" prefix="rk"%>
4) 在jsp中使用自定义标签:<rk:showIp></rk:showIp>
(1)示例代码showIP.java:
package com.rk.tag;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.SimpleTagSupport;
/**
* 标签处理器类
* @author lsieun
*
* 1)继承SimpleTagSupport
*/
public class ShowIpTag extends SimpleTagSupport
{
/**
* 2)覆盖doTag方法:向浏览器输出客户的ip地址
*/
@Override
public void doTag() throws JspException, IOException
{
//
PageContext pageContext = (PageContext) this.getJspContext();//得到JSP内置对象pageContext
HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();//通过pageContext得到request对象
String ip = request.getRemoteHost();//通过request得到IP地址
JspWriter out = pageContext.getOut();//通过pageContext对象得到out对象
out.write("使用自定义标签输出客户的IP地址:" + ip);
}
}(2)标签库声明文件示例rk.tld
<?xml version="1.0" encoding="UTF-8" ?> <taglib xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd" version="2.1"> <description>this is lsieun's library</description> <display-name>lsieun's library</display-name> <!-- 标签库的版本 --> <tlib-version>1.1</tlib-version> <!-- 标签库前缀 --> <short-name>rk</short-name> <!-- tld文件的唯一标记 --> <uri>http:///rk</uri> <!-- 一个标签的声明 --> <tag> <description> Show client's IP address. </description> <!-- 标签名称 --> <name>showIP</name> <!-- 标签处理器类的全名 --> <tag-class>com.rk.tag.ShowIpTag</tag-class> <!-- 输出标签体内容格式 --> <body-content>scriptless</body-content> </tag> </taglib>
(3)jsp引入自定义标签库
<%@taglib uri="http:///rk" prefix="rk" %>
(4)在jsp中使用自定义标签
<rk:showIP></rk:showIP>
完整的JSP文件:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <%@taglib uri="http:///rk" prefix="rk" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>显示IP地址</title> </head> <body> 一段内容<br/> <rk:showIP></rk:showIP><br/> 一段内容<br/> </body> </html>
3.2、自定义标签的执行过程
问题: http://localhost:8080/myweb/showIP.jsp 如何访问到自定义标签?
| 序号 | 执行过程 |
|---|---|
| 前提 | tomcat服务器启动时,加载到每个web应用,加载每个web应用的WEB-INF目录下的所有文件!!!例如。web.xml, tld文件!!! 也就是说,tomcat服务器已经加载rk.tld文件。 |
| 1 | 访问showIP.jsp资源 |
| 2 | tomcat服务器把jsp文件翻译成java源文件->编译class->构造类对象->调用_jspService()方法 |
| 3 | 检查jsp文件的taglib指令,是否存在一个名为http:///rk的tld文件。如果没有,则报错 |
| 4 | 上一步已经读到rk.tld文件 |
| 5 | 读到<rk:showIP>后,从rk.tld文件中查询是否存在<name>为showIP的<tag>标签 |
| 6 | 找到对应的<tag>标签,再读取<tag-class>内容 |
| 7 | 得到字符串 com.rk.tag.ShowIpTag |
| 8 | 构造ShowIpTag对象,然后调用doTag()方法 |
3.3、自定义标签处理器类的生命周期
刚才的ShowIpTag类继承自javax.servlet.jsp.tagext.SimpleTagSupport类,
而SimpleTagSupport类实现了javax.servlet.jsp.tagext.SimpleTag接口。
SimpleTag接口类的源码,如下:
package javax.servlet.jsp.tagext;
import javax.servlet.jsp.JspContext;
/**
* Interface for defining Simple Tag Handlers.
*
* <p>A SimpleTag handler must have a public no-args constructor. Most
* SimpleTag handlers should extend SimpleTagSupport.</p>
*
* <p><b>Lifecycle</b></p>
*
* <p>The following is a non-normative, brief overview of the
* SimpleTag lifecycle. </p>
*
* <ol>
* <li>A new tag handler instance is created each time by the container
* by calling the provided zero-args constructor. Unlike classic
* tag handlers, simple tag handlers are never cached and reused by
* the JSP container.</li>
* <li>The <code>setJspContext()</code> and <code>setParent()</code>
* methods are called by the container. The <code>setParent()</code>
* method is only called if the element is nested within another tag
* invocation.</li>
* <li>The setters for each attribute defined for this tag are called
* by the container.</li>
* <li>If a body exists, the <code>setJspBody()</code> method is called
* by the container to set the body of this tag, as a
* <code>JspFragment</code>. If the action element is empty in
* the page, this method is not called at all.</li>
* <li>The <code>doTag()</code> method is called by the container. All
* tag logic, iteration, body evaluations, etc. occur in this
* method.</li>
* <li>The <code>doTag()</code> method returns and all variables are
* synchronized.</li>
* </ol>
*
* @see SimpleTagSupport
* @since 2.0
*/
public interface SimpleTag extends JspTag {
/**(1)第1个调用setJspContext方法,主要是为了得到JspContext(本质上是pageContext对象)
* Called by the container to provide this tag handler with
* the <code>JspContext</code> for this invocation.
* An implementation should save this value.
*
*/
public void setJspContext( JspContext pc );
/**(2)第2个调用setParent方法,得到当前标签的父标签
* Sets the parent of this tag, for collaboration purposes.
* <p>
* The container invokes this method only if this tag invocation is
* nested within another tag invocation.
*
*/
public void setParent( JspTag parent );
/**这是一个与setParent方法相对应的方法
* Returns the parent of this tag, for collaboration purposes.
*
*/
public JspTag getParent();
/** (3)第3个调用setJspBody方法,得到当前标签的子标签
* Provides the body of this tag as a JspFragment object, able to be
* invoked zero or more times by the tag handler.
* <p>
* This method is invoked by the JSP page implementation
* object prior to <code>doTag()</code>. If the action element is
* empty in the page, this method is not called at all.
*
*/
public void setJspBody( JspFragment jspBody );
/** (4)第4个调用doTag方法,由tag library developer覆写这个方法,执行标签输出
* Called by the container to invoke this tag.
* The implementation of this method is provided by the tag library
* developer, and handles all tag processing, body iteration, etc.
*
* <p>
* The JSP container will resynchronize any AT_BEGIN and AT_END
* variables (defined by the associated tag file, TagExtraInfo, or TLD)
* after the invocation of doTag().
*
*/
public void doTag()
throws javax.servlet.jsp.JspException, java.io.IOException;
}SimpleTagSupport类源码,如下:
package javax.servlet.jsp.tagext;
import javax.servlet.jsp.JspContext;
import javax.servlet.jsp.JspException;
import java.io.IOException;
/**
* A base class for defining tag handlers implementing SimpleTag.
* <p>
* The SimpleTagSupport class is a utility class intended to be used
* as the base class for new simple tag handlers. The SimpleTagSupport
* class implements the SimpleTag interface and adds additional
* convenience methods including getter methods for the properties in
* SimpleTag.
*
* @since 2.0
*/
public class SimpleTagSupport
implements SimpleTag
{
/** Reference to the enclosing tag.父标签 */
private JspTag parentTag;
/** The JSP context for the upcoming tag invocation.一个JspContext对象,实质上pageContext对象。 */
private JspContext jspContext;
/** The body of the tag.标签的内容(标签体) */
private JspFragment jspBody;
/**(1)第1个执行构造函数
* Sole constructor. (For invocation by subclass constructors,
* typically implicit.)
*/
public SimpleTagSupport() {
}
/**(2)第2个执行setJspContext方法
* Stores the provided JSP context in the private jspContext field.
* Subclasses can access the <code>JspContext</code> via
* <code>getJspContext()</code>.
*
* @param pc the page context for this invocation
* @see SimpleTag#setJspContext
*/
public void setJspContext( JspContext pc ) {
this.jspContext = pc;
}
/**
* Returns the page context passed in by the container via
* setJspContext.
*
* @return the page context for this invocation
*/
protected JspContext getJspContext() {
return this.jspContext;
}
/**(3)第3个执行setParent方法
* Sets the parent of this tag, for collaboration purposes.
* <p>
* The container invokes this method only if this tag invocation is
* nested within another tag invocation.
*/
public void setParent( JspTag parent ) {
this.parentTag = parent;
}
/**
* Returns the parent of this tag, for collaboration purposes.
*/
public JspTag getParent() {
return this.parentTag;
}
/** (4)第4个执行setJspBody方法
* Stores the provided JspFragment.
*
* @param jspBody The fragment encapsulating the body of this tag.
* If the action element is empty in the page, this method is
* not called at all.
* @see SimpleTag#setJspBody
*/
public void setJspBody( JspFragment jspBody ) {
this.jspBody = jspBody;
}
/**
* Returns the body passed in by the container via setJspBody.
*
* @return the fragment encapsulating the body of this tag, or
* null if the action element is empty in the page.
*/
protected JspFragment getJspBody() {
return this.jspBody;
}
/** (5)第5个执行doTag方法
* Default processing of the tag does nothing.
*/
public void doTag()
throws JspException, IOException
{
}
/**
* Find the instance of a given class type that is closest to a given
* instance.
* This method uses the getParent method from the Tag and/or SimpleTag
* interfaces. This method is used for coordination among
* cooperating tags.
*
* <p> For every instance of TagAdapter
* encountered while traversing the ancestors, the tag handler returned by
* <tt>TagAdapter.getAdaptee()</tt> - instead of the TagAdpater itself -
* is compared to <tt>klass</tt>. If the tag handler matches, it - and
* not its TagAdapter - is returned.
*
* <p>
* The current version of the specification only provides one formal
* way of indicating the observable type of a tag handler: its
* tag handler implementation class, described in the tag-class
* subelement of the tag element. This is extended in an
* informal manner by allowing the tag library author to
* indicate in the description subelement an observable type.
* The type should be a subtype of the tag handler implementation
* class or void.
* This addititional constraint can be exploited by a
* specialized container that knows about that specific tag library,
* as in the case of the JSP standard tag library.
*
* <p>
* When a tag library author provides information on the
* observable type of a tag handler, client programmatic code
* should adhere to that constraint. Specifically, the Class
* passed to findAncestorWithClass should be a subtype of the
* observable type.
*
*
*/
public static final JspTag findAncestorWithClass(
JspTag from, Class<?> klass)
{
boolean isInterface = false;
if (from == null || klass == null
|| (!JspTag.class.isAssignableFrom(klass)
&& !(isInterface = klass.isInterface()))) {
return null;
}
for (;;) {
JspTag parent = null;
if( from instanceof SimpleTag ) {
parent = ((SimpleTag)from).getParent();
}
else if( from instanceof Tag ) {
parent = ((Tag)from).getParent();
}
if (parent == null) {
return null;
}
if (parent instanceof TagAdapter) {
parent = ((TagAdapter) parent).getAdaptee();
}
if ((isInterface && klass.isInstance(parent))
|| klass.isAssignableFrom(parent.getClass())) {
return parent;
}
from = parent;
}
}
}| 序号 | 方法名 | 说明 |
|---|---|---|
| 1 | void setJspContext( JspContext pc ) | 设置JspContext对象(本质上是PageContext类型),(一定会被调用) 通过getJspCotext()方法得到pageContext对象 |
| 2 | void setParent( JspTag parent ) | 设置父标签对象,传入父标签对象,如果没有父标签,则不调用此方法。通过getParent()方法得到父标签对象。 |
| 3 | void setXXX(值) | 设置属性值。 |
| 4 | void setJspBody( JspFragment jspBody ) | 设置标签体内容。标签体内容封装到JspFragment对象中,然后传入JspFragment对象。 通过getJspBody()方法得到标签体内容。如果没有标签体内容,则不会调用此方法。 |
| 5 | void doTag() | 执行标签时调用的方法。(一定会被调用) |
3.4、自定义标签的作用
1)控制标签体内容是否输出
DisplayContentTag文件:
package com.rk.tag;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;
/**
* 标签处理器类
* @author lsieun
*
*/
public class DisplayContentTag extends SimpleTagSupport
{
@Override
public void doTag() throws JspException, IOException
{
/**
* 1)控制标签内容是否输出
* 输出: 调用jspFrament.invoke();
* 不输出: 不调用jspFrament.invoke();
*/
//1.1 得到标签体内容
JspFragment jspBody = this.getJspBody();
/**
* 执行invoke方法: 把标签体内容输出到指定的Writer对象中
*/
//1.2 往浏览器输出内容,writer为null就是默认往浏览器输出
//JspWriter out = this.getJspContext().getOut();
//jspBody.invoke(out);
jspBody.invoke(null);//等价于上面的代码
}
}.tld文件配置:
<tag> <name>display</name> <tag-class>com.rk.tag.DisplayContentTag</tag-class> <body-content>scriptless</body-content> </tag>
JSP文件:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <%@taglib uri="http:///rk" prefix="rk" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>控制标签体内容是否输出</title> </head> <body> 标签前的内容==========================<br> <rk:display>这里是标签体的内容</rk:display><br/> 标签后的内容==========================<br> </body> </html>
2)控制标签余下内容是否输出
OmitFollowingContentTag.java文件:
package com.rk.tag;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.SkipPageException;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class OmitFollowingContentTag extends SimpleTagSupport
{
@Override
public void doTag() throws JspException, IOException
{
JspFragment jspBody = getJspBody();
jspBody.invoke(null);//将标签体的内容输出到浏览器
/**
* 2)控制标签余下内容是否输出
* 输出: 什么都不干!
* 不输出: 抛出SkipPageException异常
*/
throw new SkipPageException();
}
}.tld文件配置:
<tag>
<name>omitAfter</name>
<tag-class>com.rk.tag.OmitFollowingContentTag</tag-class>
<body-content>scriptless</body-content>
</tag>
JSP文件:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <%@taglib uri="http:///rk" prefix="rk" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>控制标签余下内容是否输出</title> </head> <body> 标签前的内容==========================<br> <rk:omitAfter>这里是标签体的内容</rk:omitAfter> 标签后的内容========================== <br> </body> </html>
3)控制重复输出标签体内容
DisplayNContentTag.java文件:
package com.rk.tag;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class DisplayNContentTag extends SimpleTagSupport
{
//1.声明属性的成员变量
private Integer num;
//2.关键点: 必须提供公开的setter方法,用于给属性赋值
public void setNum(Integer num)
{
this.num = num;
}
@Override
public void doTag() throws JspException, IOException
{
//1. 得到标签体内容
JspFragment jspBody = this.getJspBody();
/**
* 2.控制重复输出标签体内容
* 方法: 执行多次jspBody.invoke(null)方法
*/
for(int i=1;i<=num;i++){
jspBody.invoke(null);
}
}
}.tld文件配置:
<tag> <name>displayN</name> <tag-class>com.rk.tag.DisplayNContentTag</tag-class> <body-content>scriptless</body-content> <!-- 属性声明 --> <attribute> <!-- 属性名称 --> <name>num</name> <!-- 是否必填 --> <required>true</required> <!-- 是否支持EL表达式 --> <rtexprvalue>false</rtexprvalue> <!-- 接受的数据类型 --> <type>java.lang.Integer</type> </attribute> </tag>
JSP文件:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <%@taglib uri="http:///rk" prefix="rk" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>控制重复输出标签体内容</title> </head> <body> 标签前的内容==========================<br> <rk:displayN num="3">A</rk:displayN><br/> 标签后的内容========================== <br> </body> </html>
4)改变标签体内容
ChangeContentTag.java文件:
package com.rk.tag;
import java.io.IOException;
import java.io.StringWriter;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class ChangeContentTag extends SimpleTagSupport
{
@Override
public void doTag() throws JspException, IOException
{
// 得到标签体内容
JspFragment jspBody = getJspBody();
/**
* 改变标签体内容
*/
//1. 创建StringWriter临时容器
StringWriter sw = new StringWriter();
//2. 把标签体拷贝到临时容器
jspBody.invoke(sw);
//3. 从临时容器中得到标签体内容
String content = sw.toString();
//4. 改变内容
content = content.toLowerCase();
//System.out.println(content);
//5. 把改变的内容输出到浏览器
//jspBody.invoke(null); 不能使用此方式输出,因为jsbBody没有改变过
this.getJspContext().getOut().write(content);
}
}.tld文件配置:
<tag> <name>change</name> <tag-class>com.rk.tag.ChangeContentTag</tag-class> <body-content>scriptless</body-content> </tag>
JSP文件:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <%@taglib uri="http:///rk" prefix="rk" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>控制重复输出标签体内容</title> </head> <body> 标签前的内容==========================<br> <rk:change>ABCDEFG</rk:change><br/> 标签后的内容========================== <br> </body> </html>
5)带属性的标签
3.5、输出标签体内容格式
JSP: 在传统标签中使用的。可以写和执行jsp的java代码。
scriptless: 标签体不可以写jsp的java代码
empty: 必须是空标签。
tagdependent : 标签体内容可以写jsp的java代码,但不会执行。
















