1. 程式人生 > >使用tld檔案自定義jsp標籤庫

使用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標籤的介紹,通過此功能可以組建自己更強大更高效的標籤庫。