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