天天看點

自定義标簽基礎知識

showbook.php?id=100338&v=art
8.1 自定義标簽基礎知識

自定義标簽基礎知識

1.自定義标簽(Custom Tag)概述

<!--[if !supportLists]-->(1)使用者自定義的一種jsp标記。自定義标簽将那些重複工作進行封裝,進而提高了工程生産力,而且将具有共用特性的标簽庫應用于不同的項目中,展現了軟體複用的思想。

<!--[if !supportLists]-->(2)當一個含有自定義标簽的jsp頁面被jsp引擎(Web容器)編譯成servlet時,标簽将被轉化成對一個稱為tag處理類的對象的操作。之後當JSP頁面對應的servlet被執行時,jsp引擎就調用這些操作。

2.标簽的形式

<字首:标簽名 屬性名1=值1屬性名2=值2 …>标簽主體</字首:标簽名>

一個标簽可以沒有屬性或者沒有主體,也可以同時沒有屬性和主體。沒有主體的标簽可以簡寫成這樣的形式:<字首:标簽名 屬性名1=值1 …/>、<字首:标簽名/>。

3.自定義标簽的優點

<!--[if !supportLists]-->(1)分離了程式邏輯和表示邏輯。

<!--[if !supportLists]-->(2)将Java代碼從HTML中剝離,便于美工維護頁面。

<!--[if !supportLists]-->(3)減少了JSP頁面中的腳本,減少了維護成本。

<!--[if !supportLists]-->(4)提供了可重用的功能元件。

4.自定義标簽庫(Custom Tag library)

由一系列功能相似、邏輯上互相聯系的自定義标簽構成的集合稱為标簽庫。

5.建立自定義标簽的方式

(1)Java類檔案方式。

(2)标簽檔案方式。

标簽檔案是一個擴充名為.tab的檔案,它必須存儲在Web應用程式的WEB-INF目錄中。标簽檔案的作用類似标簽處理檔案,它是一個包含一些内容或JSP代碼的要重用的JSP片段。在JSP頁面遇到自定義标簽時,它會轉到标簽檔案以執行标簽定義。不需要一個單獨的标簽庫描述符,因為标簽檔案包含自定義标簽的完整實作。

Java類檔案方式建立和使用一個自定義标簽庫的基本步驟有如下幾步。

(1)建立标簽的處理類(Tag Handle Class)。定義标簽的行為,并在JSP引擎遇到自定義标簽時調用執行。标簽處理類是一個Java類,這個類繼承了TagSupport或者擴充了SimpleTag接口,通過這個類可以實作自定義JSP标簽的具體功能。

(2)建立标簽庫描述檔案TLD(Tag Library Descrptor File)。描述标簽庫的XML文檔,向JSP引擎提供有關自定義标簽的标簽處理程式的資訊。

(3)在web.xml檔案中聲明TLD的位置。

(4)在JSP檔案中用taglib指令引入标簽庫。

(5)在JSP中使用标簽庫标簽。

6.建立标簽的處理類

(1)傳統标簽處理器在javax.servlet.jsp.tagext包中的接口和類,如圖8-1所示。

<!--[if !vml]--><!--[endif]-->

圖8-1  傳統标簽的接口和類

我們要寫的自定義标簽處理類主要繼承自TagSupport、BodyTagSupport這兩個類。通過繼承這兩個類,隻需要重新定義那些需要自定義的行為的方法,進而簡化了标簽處理程式的開發。

(2)簡單标簽處理器javax.servlet.jsp.tagext包中的接口和類。我們要寫的自定義标簽處理類繼承自SimpleTagSupport類,重寫doTag()方法。這是在JSP2.0新規範中增加的類,這個類實作自SimpleTag接口,之是以稱其為“簡單”是指它相對于傳統标簽處理器實作任務要簡單得多。

(3)TagSupport類。TagSupport類并不能對标簽主體的内容做任何處理,您所能決定的隻是是否顯示主體的内容。

① 生命周期

當JSP容器在解釋JSP頁面時,如果碰到自定義标簽,将利用“标簽處理類”建立一個“标簽處理對象”。在建立“标簽處理對象”的過程中,JSP容器會根據自定義标簽的屬性值來初始化“标簽處理對象”的屬性。

首先,JSP容器會運作doStartTag()方法内的程式代碼,然後根據此方法的傳回值決定後續動作。如果傳回SKIP_BODY,表示要求JSP容器忽略标簽主體的内容;如果傳回EVAL_BODY_INCLUDE,表示要求JSP容器要顯示标簽主體的内容,然後運作doAfterBody()方法。

如果doAfterBody()方法傳回EVAL_BODY_AGAIN,表示要求JSP容器再次顯示标簽主體的内容,如果傳回SKIP_BODY,JSP容器将會運作doEndTag()方法。

最後,JSP容器會運作doEndTag()方法内的程式代碼,并根據此方法的傳回值決定後續動作:如果傳回SKIP_PAGE,JSP容器會運作release()方法,然後忽略自定義标簽以後的JSP内容;如果傳回EVAL_PAGE,JSP容器會先運作release()方法,然後運作自定義标簽以後的JSP内容。

② 生命周期主要方法

●  doStartTag()方法可傳回的值有SKIP_BODY,忽略标簽主體的内容(此方法的預設傳回值);VAL_BODY_INCLUDE,表示要求JSP容器要顯示标簽主體内容。

●  doAfterBody()方法可傳回的值有以下幾項。

●  SKIP_BODY:要求JSP容器忽略主體内容,進入标簽處理程式的下一步工作(此方法的預設傳回值)。

●  EVAL_BODY_AGAIN:要求JSP容器再次顯示标簽主體内容。

●  doEndTag()方法可傳回的值有以下幾項

●  SKIP_PAGE:忽略自定義标簽以後的JSP網頁内容。

●  EVAL_PAGE:運作自定義标簽以後的JSP網頁内容(此方法的預設傳回值)。

●  release():釋放标簽處理對象所占用的系統資源。

(4)BodyTagSupport類。

BodyTagSupport與TagSupport的差別主要是标簽處理類是否需要讀取标簽體的内容和改變标簽體傳回的内容,如果不需要互動的就用TagSupport,否則就用BodyTagSupport。

這裡需要注意的是TagSupport也可以有體,如果将TagSupport了解成是沒有體的标簽,而将BodyTagSupport了解成是有體的标簽就錯了。當然用TagSupport實作的标簽,都可以用BodyTagSupport來實作,道理很簡單,因為BodyTagSupport繼承了TagSupport。

●  doStartTag() 方法可傳回的值有SKIP_BODY,表示要求JSP容器忽略主體内容;

EVAL_BODY_INCLUDE,表示要求JSP容器要顯示标簽主體内容;EVAL_BODY_BUFFERED 表示JSP容器會将标簽主體的處理結果建立成一個BodyContent對象(此方法的預設傳回值)。

●  setBodyContent()方法會設定BodyContent對象的一些屬性,包括标簽主體的内容和在處理标簽過程中要輸出至response的資料對象。

●  doInitBody()方法在第一次處理标簽主體内容時,它将對主體進行初始化的工作。

●  doAfterBody()方法可傳回的值有SKIP_BODY,表示要求JSP容器忽略主體,進入下一步的處理工作;EVAL_BODY_AGAIN,表示要求JSP容器再次處理标簽主體。

●  doEndTag()方法可傳回的值有SKIP_PAGE,表示忽略自定義标簽以後的JSP網頁内容; EVAL_PAGE,表示運作自定義标簽以後的JSP網頁内容。

●  release()方法釋放标簽處理對象所占用的系統資源。

(5)SimpleTagSupport類。

生命周期事件有以下幾種。

●  當JSP容器遇到标記時,将标簽處理類的預設構造方法建立一個标簽處理執行個體。注意必須為每個标記都建立一個新的執行個體,這很重要。

●  在标記處理程式中調用setJspContext()和setParent()方法。如果傳遞的值是“null”,則不需要調用setParent()方法。在使用标記檔案的情況下,建立一個JspContext包裝,以便标記檔案看上去具有其本身的頁面範圍。調用getJspContext()必須傳回所包裝的JspContext。

●  容器為每個标記所定義的屬性,同時也需要提高set方法,set方法是用來讓外界設定屬性的值,其順序是它們出現在JSP頁或标記檔案中的順序。如果屬性值是表達式語言的表達式或運作時表達式,則它首先被指派,然後被傳遞到設定器;另一方面,如果屬性為動态屬性,則調用setDynamicAttribute()。

●  由容器調用setJspBody()方法,将該标記的主體設定為JspFragment;如果标記被聲明為具有空的值,則将null值傳遞到setJspBody()。

●  由容器調用doTag()方法。所有的标記邏輯、疊代、主體指派等都在該方法中發生。

●  在doTag()方法傳回後,所有的變量被同步。

7.撰寫标簽處理類

你所撰寫的标簽處理類應該具備下列特性:

●  承TagSupport類或BodyTagSupport類或SimpleTagSupport類;

●  對自定義标簽的屬性聲明相對應的變量,并提供一個setXXX()方法;

●  doStartTag()、doAfterBody()或doEndTag()方法(或doTag()方法)内撰寫Java程式代碼,實作自定義标簽欲提供的功能。

示例如下:

Welcome.java

package com.mapgis.vfd.taglib;

import javax.servlet.jsp.tagext.BodyTagSupport;

import javax.servlet.jsp.tagext.BodyContent;

import javax.servlet.jsp.JspWriter;

import java.io.IOException;

public class Welcome extends BodyTagSupport {

public int doAfterBody() {

try {

//獲得标簽體的内容

BodyContent bodyContent = super.getBodyContent();

String bodyString = bodyContent.getString();

//擷取輸出流對象

JspWriter out = bodyContent.getEnclosingWriter();

out.print(bodyString.toUpperCase());

bodyContent.clear();

} catch (IOException e) {

System.out.println("BodyContentTag.doAfterBody() 中出現錯誤" + e.getMessage());

e.printStackTrace();

}

return this.SKIP_BODY;

}

}

8.建立标簽庫描述檔案

标簽庫描述檔案(Tag Library Discriptor,TLD)是一份标準的XML檔案,用來記錄一個标簽庫内擁有哪些标簽?每個标簽包含哪些屬性?取得這些資訊後,JSP容器才能正确地處理并運作JSP所包含的自定義标簽。以下是一個JSP2.0規範的TLD:

weclome.tld

<?xml version="1.0" encoding="UTF-8"?>

<!-- 标簽庫描述符 -->

<taglib xmlns="http://java.sun.com/xml/ns/j2ee"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"

version="2.0">

<!-- 此标簽庫的一個描述 -->

<description>A tag library exercising SimpleTag handlers.</description>

<!-- 此标簽庫的版本,由程式設計師自行決定(必須) -->

<tlib-version>1.0</tlib-version>

<!-- 定義一個簡短的名稱(必須)主要由工具使用 -->

<short-name>SimpleTagLibrary</short-name>

<!-- 定義該标簽庫的唯一uri名(在taglib指令中使用) -->

<uri>/SimpleTagLibrary</uri>

<tag><!-- 定義每個标簽的特性 -->

<description>body content to Upper</description><!-- 描述 -->

<name>simpletag</name> <!-- 自定義标簽的名稱 -->

<tag-class>xmh.comchapter11.Welcome</tag-class> <!-- 此标簽對應的處理類 -->

<body-content>scriptless</body-content><!-- 定義标簽主體的種類:指定标簽的格式,empty表示标簽沒有标簽主體;JSP表示标簽的标簽體中可以包含JSP代碼;scriptless表示标簽主體中可以包含EL表達式和JSP的動作元素,但不能包含JSP的腳本元素;tagdependent表示标簽的标簽主體交由标簽本身去解析處理。如果指定為它,那麼你在标簽體中所寫的任何代碼都會原封不動地傳給标簽處理器,而不是傳遞執行的結果。 -->

<attribute>

<name>attrName</name> <!-- 此屬性的名稱 -->

<required>false</required> <!-- 此屬性是否為必要:true | false -->

<rtexprvalue>true</rtexprvalue><!-- 屬性值是否可以在JSP運作時期動态産生:true | false -->

</attribute>

</tag>

</taglib>

9.在web.xml檔案中聲明TLD的位置

<web-app xmlns="http://java.sun.com/xml/ns/j2ee"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">

......

<jsp-config>

<taglib>

<!-- 指定TLD檔案所對應的URI -->

<taglib-uri>/mytag</taglib-uri>

<!-- 指定TLD檔案的存放路徑 -->

<taglib-location>/WEB-INF/tlds/weclome.tld</taglib-location>

</taglib>

</jsp-config>

</web-app>

注意在JSP2.0規範中可以不用聲明TLD的位置,因為容器開始建立<uri>和TLD映射時,會首先在web.xml中查找,看是否有<taglib>項,如果确實有,就會使用這些設定來幫助建立映射。否則會在WEB-INF目錄以及它的tlds子目錄(還可以是jar檔案中)下查找TLD檔案,然後自動建立TLD檔案和<uri>名之間的映射關系。

10.在JSP檔案中用taglib指令引入标簽庫

<%@ taglib uri="/mytag" prefix="my" %>

11.在JSP中使用标簽庫标簽

<my:simpletag>Hello,World!</my:simpletag>

12.自定義JSP标簽的處理過程

<!--[if !supportLists]-->(1)在JSP中引入标簽庫:<% @ taglib prefix="taglibprefix" uri="tagliburi" %>。

<!--[if !supportLists]-->(2)在JSP中使用标簽庫标簽:<prefix : tagname attribute="tagattribute">。

<!--[if !supportLists]-->(3)Web容器根據第二個步驟中的prefix,獲得第一個步驟中聲明的taglib的uri屬性值。

<!--[if !supportLists]-->(4)Web容器根據uri屬性在web.xml找到對應的<taglib>元素。

<!--[if !supportLists]-->(5)從<taglib>元素中獲得對應的<taglib-location>元素的值。

<!--[if !supportLists]-->(6)Web容器根據<taglib-location>元素的值從WEB-INF/目錄下找到對應的.tld檔案。

<!--[if !supportLists]-->(7)從.tld檔案中找到與tagname對應的<tag>元素。

<!--[if !supportLists]--><!--[endif]-->(8)從<tag>元素中獲得對應的<tag-class>元素的值。

<!--[if !supportLists]-->(9)Web容器根據<tag-class>元素的值建立相應的标簽處理類的執行個體。

<!--[if !supportLists]-->(10)Web容器調用這個執行個體的doStartTag/doAfterBody/doEndTag方法完成相應的處理。