使用tld文件自定义jsp标签库

目录

一,标签库描述文件(tld文件)

二,标签处理类

三,在JSP页面中使用自定义的标签

四,举个例子


通过Java的TagSupport类或者BodyTagSupport类,和配套的tld文件,可以定义自己的jsp标签。

TagSupport类和BodyTagSupport类在jsp-api.jar中,这个jar包在tomcat的lib目录下有,maven里面也有。

 

一,标签库描述文件(tld文件)

tld的全名:Tag Library Descriptor

tld文件是一个XML文件,是自定义标签库的配置文件。

这个文件应该放在web项目的WEB-INF目录下,或其子目录下,如果放在其他位置,需要在web.xml文件中做配置。

 

下面是一个简单的标签库描述文件的例子:

myFirstTag.tld

<?xml version="1.0" encoding="UTF-8"?>
<!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>testTagLib</short-name>
    <uri>/test-tags</uri>
    <display-name>"自定义标签"</display-name>
    <tag>
        <name>TagA</name>
        <tag-class>com.mytest.TagA</tag-class>
        <body-content>jsp</body-content>
        <description>这个一个标签</description>
        <attribute>
            <name>attributeA</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
            <description>属性A</description>
        </attribute>
    </tag>
    <tag>
        <name>TagB</name>
        <tag-class>com.mytest.TagB</tag-class>
        <body-content>jsp</body-content>
        <description>这个一个标签</description>
        <attribute>
            <name>attributeA</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
            <description>属性A</description>
        </attribute>
    </tag>
</taglib>

文件的主体是<tablib>元素,其中的元素有:

<tlib-version>,taglib版本

<jsp-version>,jsp版本

<short-name>,标签库名字

<uri>,标签库地址,在页面使用标签库时会用到

<display-name>,描述

<tag>,具体的标签列表,可以配置多个

 

其中的<tag>元素,每个元素代表一个自定义的标签,可以有这些子元素:

<name>,标签名字,在页面使用标签时会用到

<tag-class>,标签处理类

<body-content>,empty、scriptless、JSP、tagdependent

<description>,标签描述,可以配置多个

<attribute>,标签参数列表,可以配置多个

 

其中的<attribute>元素,每个元素代表一个属性,可以有这些子元素:

<name>,属性名字,在页面使用标签时会用到

<required>,是否必填,只能是true或false

<rtexprvalue>,全称Run-time Expression Value,是否支持EL表达式,只能是true或false。如果设置为true,在页面可以用这种写法:attributeA="<%=name>"

<description>,属性描述

 

二,标签处理类

标签处理类是在描述文件中用<tag-class>元素标识出的java类,这个类可以选择继承javax.servlet.jsp.tagext.TagSupport类或javax.servlet.jsp.tagext.BodyTagSupport类,其中BodyTagSupport是TagSupport的子类。

如果选择继承TagSupport类,可以重写以下方法:

public int doStartTag()

public int doEndTag()

public int doAfterBody()

如果选择继承BodyTagSupport类,可以额外重写以下方法:

public void setBodyContent(BodyContent b)

public void doInitBody()

 

下面分别介绍这几个方法:

doStartTag()

这是在java开始处理头标签时执行的方法,可以在这里构造将要输出到页面的代码,或者做其他的处理。

这个方法的返回值是int,有以下选择:

1,EVAL_BODY_INCLUDE。

这个静态变量在javax.servlet.jsp.tagext.Tag接口中,实际值是1。

这个返回值表示正常加载标签的body(也就是标签的innerHTML)。

2,SKIP_BODY。

这个静态变量在javax.servlet.jsp.tagext.Tag接口中,实际值是0。

这个返回值表示不会加载标签的body。一般情况下doStargTag()方法返回这个值后将执行doEndTag()方法。

3,EVAL_BODY_BUFFERED。

这个静态变量在javax.servlet.jsp.tagext.BodyTag接口中,实际值是2。

只有处理类继承了BodyTagSupport时可以使用(详见下面的类继承关系图)。

这个返回值用来在后面的代码中处理标签的body。当使用这个返回值时,Java会构造一个BodyContent对象,并把标签的body载入这个对象。

另外,BodyContent对象内部也有一个JspWriter,可以用BodyContent对对象的body进行处理。是否可以修改body是BodyTagSupport和TagSupport最大的区别。

注意:如果使用了这个返回值并重写了相关方法,需要在代码中显式写明body部分的加载,否则body只会写入BodyContent对象而不会加载到页面。

 

doEndTag()

这是在java开始处理尾标签时执行的方法,这个方法中也可以构造将要输出到页面的代码,或者其他处理。

这个方法的返回值是int,有以下选择:

1,EVAL_PAGE。

这个静态变量在javax.servlet.jsp.tagext.Tag接口中,实际值是6。

这个返回值表示继续加载此标签之后的页面代码。

2,SKIP_PAGE。

这个静态变量在javax.servlet.jsp.tagext.Tag接口中,实际值是5。

这个返回值表示不再继续加载此标签之后的页面代码,直接将jsp现有已经处理完的页面代码发送给客户端。

没有想到什么情况下用得着这个返回值。

 

doAfterBody()

这个方法在doStartTag()方法返回EVAL_BODY_INCLUDE并且加载完标签的body后加载。

这个方法的返回值是int,有以下选择:

1,EVAL_BODY_AGAIN。

这个静态变量在javax.servlet.jsp.tagext.IterationTag接口中,实际值是5。

这个返回值表示再次加载标签的body。

按照执行顺序,再次加载一次body之后会再次执行这个方法,注意不要写成死循环。

2,SKIP_BODY。

同doStartTag()方法的同名返回值。

java不会再加载标签的body,向后执行。一般情况下后面将执行doEndTag()方法。

 

setBodyContent(BodyContent b)

这个方法是在调用doStartTag()方法,并返回EVAL_BODY_BUFFERED之后调用的。BodyTagSupport子类专用。

Java构造了BodyContent对象之后调用这个方法把BodyContent对象传给处理类,BodyTagSupport类中这个方法里面只有一行:

this.bodyContent = b;

如果重写了这个方法,至少也要把这行加上,否则无法使用BodyContent对象。

该方法没有返回值。

 

doInitBody()

该方法在调用setBodyContent(BodyContent b)方法后执行,可以对body进行一些初始化工作。BodyTagSupport子类专用。

BodyTagSupport类中这个方法里面没有代码。

这个方法没有返回值。

 

上述类和接口的关系图大概是这样的:

 

当java处理标签时,根据返回值的不同,方法的调用顺序大概是这样的:

 

三,在JSP页面中使用自定义的标签

1,在JSP页面中,首先要引用标签库:

<%@ taglib prefix="t" uri="/test-tags"%>

prefix是标签前缀,和标签库中的<short-name>不一定相同。

uri参数和标签库中的<uri>必须相同。

2,引用了标签库之后,就可以使用自定义的标签了:

<testTagLib:TagA attributeA="extra">

         number2:<input id='number2' name='number2' value='222' />

</testTagLib:TagA>

上面的例子就是有body的标签,body就是

number2:<input id='number2' name='number2' value='222' />

这一部分,通过调整方法的返回值可以控制该部分是否显示。

 

四,举个例子

1,首先是tld标签描述文件

myFirstTag.tld

<?xml version="1.0" encoding="UTF-8"?>
<!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>testTagLib</short-name>
    <uri>/test-tags</uri>
    <display-name>"自定义标签"</display-name>
    <tag>
        <name>TagA</name>
        <tag-class>com.mytest.TagA</tag-class>
        <body-content>jsp</body-content>
        <description>这是一个标签</description>
        <attribute>
            <name>attributeA</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
            <description>属性A</description>
        </attribute>
    </tag>
    <tag>
        <name>TagB</name>
        <tag-class>com.mytest.TagB</tag-class>
        <body-content>jsp</body-content>
        <description>这是一个标签</description>
        <attribute>
            <name>attributeA</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
            <description>属性A</description>
        </attribute>
    </tag>
</taglib>

在这个描述文件中定义了两个标签,TagA和TagB

 

2,标签TagA的处理类TagA.java

TagA.java

package com.mytest;

import java.io.IOException;

import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyContent;
import javax.servlet.jsp.tagext.BodyTagSupport;
import javax.servlet.jsp.tagext.TagSupport;

public class TagA extends BodyTagSupport{
	
	private static final long serialVersionUID = 1L;
	
	protected String attributeA;

	public String getAttributeA() {
		return attributeA;
	}

	public void setAttributeA(String attributeA) {
		this.attributeA = attributeA;
	}
	
	@Override
	public int doStartTag(){
		JspWriter w=this.pageContext.getOut();
		StringBuffer sb = new StringBuffer();
		sb.append("<form id='aform' action='");
		sb.append(attributeA);
		sb.append(".do' method='post'>");
		sb.append("number1:<input id='number1' name='number1' value='111' /><br/>");
		
		try {
			w.print(sb.toString());
			w.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		if("extra".equals(attributeA)){
			return EVAL_BODY_INCLUDE;
//			return EVAL_BODY_BUFFERED;
		}else{
			return SKIP_BODY;
		}
	}
	
	@Override
	public void setBodyContent(BodyContent bodyContent) {  
        System.out.println("setBodyContent...");  
        this.bodyContent = bodyContent;  
        
    } 
	
	@Override
	public void doInitBody() throws javax.servlet.jsp.JspException {
		
	};
	
	
	@Override
	public int doAfterBody() throws javax.servlet.jsp.JspException {
		String a="";
		if(this.bodyContent!=null&&a.equals("")){
			String html=this.bodyContent.getString();
			System.out.println(html);
			
			return SKIP_BODY;
		}else{
			return SKIP_BODY;
		}
	}
	
	@Override
	public int doEndTag(){
		JspWriter w=this.pageContext.getOut();
		StringBuffer sb = new StringBuffer();
		sb.append("<br/><button type='submit'>提交</button>");
		sb.append("</form>");
		
		try {
			w.print(sb.toString());
			w.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		if("extra".equals(attributeA)){
			return EVAL_PAGE;
		}else{
			return SKIP_PAGE;
		}
	}
}

这个类重写了doStartTag(),doEndTag()和doAfterBody()等方法,其中doAfterBody()方法里面没写什么东西,和父类的原方法保持一致。

这个处理类的功能是把自定义的标签写成了一个form表单,并添加了名为number1的文本框,当属性attributeA等于extra时显示number2文本框,并且正常加载标签后面的网页内容,否则不会显示number2文本框,并且立即结束页面加载,发送给客户端。

 

3,JSP页面

testTagPage.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="testTagLib" uri="/test-tags"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>TestTagPage</title>
</head>
<body>
extra
<testTagLib:TagA attributeA="extra">
	number2:<input id='number2' name='number2' value='222' />
</testTagLib:TagA>
number3:<input id='number3' name='number2' value='333' />

<hr/>

no extra
<testTagLib:TagA attributeA="noextra">
	number2:<input id='number2' name='number2' value='222' />
</testTagLib:TagA>
number3:<input id='number3' name='number2' value='333' />

</body>
</html>

这个页面显示出来是这样的:

分析一下:

当java第一次加载TagA标签时(分隔线上面部分),属性attributeA等于extra,

doStartTag()方法返回值是EVAL_BODY_INCLUDE,表示正常加载body部分的页面代码,

doEndTag()方法返回值是EVAL_PAGE,表示正常加载标签后面的内容,所以第二个TagA标签才得以加载。

当java第二次加载TagA标签时(分隔线下面部分),属性attributeA等于noextra,

doStartTag()方法返回值是SKIP_BODY,表示不会加载body部分的页面代码,

doEndTag()方法返回值是SKIP_PAGE,表示不会加载标签后面的内容,所以第二个TagA标签后面的number3标签和</body></html>标签都没有加载。

浏览器实际收到的页面代码是这样的:

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

<title>TestTagPage</title>

</head>

<body>

extra

<form id='aform' action='extra.do' method='post'>number1:<input id='number1' name='number1' value='111' /><br/>

         number2:<input id='number2' name='number2' value='222' />

<br/><button type='submit'>提交</button></form>

number3:<input id='number3' name='number2' value='333' />

<hr/>

no extra

<form id='aform' action='noextra.do' method='post'>number1:<input id='number1' name='number1' value='111' /><br/><br/><button type='submit'>提交</button></form>

 

以上就是自定义JSP标签的介绍,通过此功能可以组建自己更强大更高效的标签库。

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页