返回值????????????????????????????????意義
SKIP_BODY?????????????????????????表示不用處理標簽體,直接調用doEndTag()方法。
SKIP_PAGE??????????????????????????忽略標簽后面的JSP內容。
EVAL_PAGE?????????????????????????處理標簽后,繼續處理JSP后面的內容。?
EVAL_BODY_BUFFERED?????????表示需要處理標簽體。
EVAL_BODY_INCLUDE???????????表示需要處理標簽體,但繞過setBodyContent()和doInitBody()方法
EVAL_BODY_AGAIN??????????????對標簽體循環處理。?


標簽庫Taglib

標簽被定義和分布在一個稱為標簽庫的結構中,一個標簽庫是由元信息和類組成的集合:

1.標簽處理器(JAVA):實現定制標簽功能的Java類。

2.標簽附加信息(TEI):向JSP容器提供編輯以確認標簽屬性和創建變量的類。

3.標簽庫描述器(TLD):描述單個標簽和整個標簽庫屬性的XML文檔。

標簽處理器(JAVA)和標簽附加信息(TEI)需要定位在 JSP容器類 載入器 可以找到的地方。標簽庫描述器(TLD)可在URL指定的符意位置。

一、標簽實現

1. 開發步驟

a.定義標簽的名字、屬性、聲明的變量和標簽體的內容。

b.編寫標簽庫描述器TLD。

c.編寫標簽處理器。

d.在JSP頁面中使用標簽。


2. JSP頁面在JSP容器中的轉換步驟:

JSP頁面存在三種形式:

jsp文件
java文件
class文件

a.指令元素、和向JSP容器提供轉換時信息。

b.HTML行在_jspService()方法中依順序轉換到out.print()語名中。

c.腳本元素的聲明被原封不動地復制到_jspService()方法外的源碼中。

d.腳本元素的表達式在_jspService()方法中依順序轉換到out.print()語名中。

e.腳本元素的Scriptlet被原封不動地復制到_jspService()方法中。

f.行為元素被轉換為執行其功能的運行時邏輯代碼。

g.定制標簽被擴展到調用其相應標簽處理器中方法的Java語句中。


3.標簽在JSP容器中的轉換步驟:

a.JSP容器使用taglib指令元素定位標簽庫描述器,將頁面中用到的定制標簽和TLD相匹配。

b.讀取標簽庫描述器的標簽列表和每一標簽相關的類名字。

c.在頁面中遇到一個標簽時,查找與具有指定名字的標簽前綴相關的一個標簽庫。

d.容器使用在TLD中找到的標簽結構信息生成一系列完成標簽功能的Java語句。


二、標簽庫描述器(TLD)

標簽庫描述器是一個描述 整個標簽庫標記信息庫中每個標簽處理器其屬性 的XML文檔。

標簽庫描述器的DTD由一個簡單的元素組成,此元素包含下列一些子元素。

1 : 整個標簽庫標記信息

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

標簽庫版本號。是一個點式十進制數,最多為4組小數點分隔的數字組成。

?? < jsp - version > 1.2 </ jsp - version >

標簽庫所需的JSP規范最低版本。例如JSP1.1

?? < short - name > wentao </ short - name >

標簽庫的縮寫名。JSP可以使用該名字作為庫中標簽的缺省前綴。

< uri > http: // www.wentao.com/taglibs/wentao</uri>

標簽庫唯一URI的元素。典型URL位置來自可下載taglib的位置。

< description > wentao Tage Liberary. </ description >

標簽庫描述信息。

2 : 每個標簽處理器及其屬性 (以displaytag-11.tld為例)

???? < tag >
????????
< name > table </ name >
????????
< tagclass > org.displaytag.tags.TableTag </ tagclass >
????????
< teiclass > org.displaytag.tags.TableTagExtraInfo </ teiclass >
????????
< bodycontent > JSP </ bodycontent >
????????
< info > my?name?is?liuwentao </ info >
????????
< attribute >
????????????
< name > list </ name >
????????????
< required > false </ required >
????????????
< rtexprvalue > true </ rtexprvalue >
????????
</ attribute >
????.

????
</ tag >

tag在TLD中加入標簽,描述組成庫的每個標簽。

???????? < name > table </ name >

name與標簽庫的名字前綴一起使用的標簽的名字,是JSP容器唯一的標簽標識 <wentao:table/>。

< tagclass > org.displaytag.tags.TableTag </ tagclass >

tagclass實現標簽的標簽處理器類的全名。

< teiclass > org.displaytag.tags.TableTagExtraInfo </ teiclass >

teiclass標簽附加信息(TEI)類的全名。TEI類給出關于標簽處理器創建變量及對標簽屬性性執行的任意有效性驗證的信息。

???????? < bodycontent > JSP </ bodycontent >

bodycontent描述標簽處理器如何使用標簽體的內容。有三種取值:

empty:表示標簽體必須為空;
JSP:表示腳本元素和模板及其它標簽一樣被評估。
tagdependent:體內容被原封不動寫入BodyContent,其它腳本元素以源碼形式出現,而不被JSP容器解釋。

?

???????? < info > my?name?is?liuwentao </ info >

info標簽的人工可讀描述性信息。

?????<attribute>
??????????? <name>list</name>
??????????? <required>false</required>
??????????? <rtexprvalue>true</rtexprvalue>
??????? </attribute>

attribute使用標簽時被編碼的屬性信息。用于定義標簽的屬性。


3?:屬性:

???????????? <name>list</name>

屬性的名字。

??????????? <required>false</required>

true|false:屬性在標簽用到的位置是否要被編碼。

??????????? <rtexprvalue>true</rtexprvalue>

true|false:屬性值能否用表達式指定。


三、標簽處理器

標簽處理器是通過實現JSP容器調用的一系列預定義方法執行定制標簽行為的一個Java類。

標簽處理器實現了標簽的行為,標簽處理器是Java類。

1. 標簽處理器的工作方式

a.導入javax.servlet.jsp和javax.servlet.jsp.tagext包。

b.實現javax.servlet.jsp.tagext包中的Tag接口或BodyTag接口。BodyTag是Tag的子接口。

c.繼承TagSupport類或BodyTagSuppoert類。它們是上述接口的缺省實現。

d.重載public int doStartTag() throws JspException方法。


2.標簽處理器的接口與實現

javax.servlet.jsp.tagext.Tag是實現標簽的最基本的接口。

javax.servlet.jsp.tagext.TagSupport是實現Tag接口的具體類。

通常情況下繼承tagSupport類而不直接實現Tag接口通常是有益的。除了對所有必需方法提供了缺省實現外、還保存了pageContext對象及對嵌套標簽的支持。

Tag接口包含4個常量 ,表示doStartTag()和doEndTag()方法可能的返回碼。

?????????//?表示需要處理標簽體。?
????????int?EVAL_BODY_INCLUDE?=?1;

EVAL_BODY_INCLUDE 當doStartTag()返回時,指明servlet應對 標簽體 進行評估。

?????//?處理標簽后,繼續處理JSP后面的內容。
????int?EVAL_PAGE?=?6;

EVAL_PAGE當doEndTag()返回時,指明頁面其余部分應被評估。


???? //表示不用處理標簽體,直接調用doEndTag()方法。
????int?SKIP_BODY?=?0;

SKIP_BODY當doStartTag()返回時,指明servlet應忽視標簽體。

?????//忽略標簽后面的JSP內容。
????int?SKIP_PAGE?=?5;

SKIP_PAGE當doEndTag()返回時,指明頁面其余部分就被跳過。


注 :body 這2個是 doStartTag()方法里面調用的。
????????? page 這2個是? doEndTag()方法里面調用的。


Tag接口的方法

1 :生成的servlet在請求處理器執行其它任務前首先調用此方法

? public ? void ?setPageContext(javax.servlet.jsp.PageContext?pageContext)?

,實現類應保存上下文對象以便它可以在標簽生命期中使用,從頁面上下文中標簽處理器可以訪問所有JSP隱含對象。


2 :使用一個標答可以找到操作棧中它上面的標簽

public ? void ?setParent(javax.servlet.jsp.tagext.Tag?tag)

。在setPageContext后立即調用

? public ?javax.servlet.jsp.tagext.Tag?getParent()

返回父標簽。

3 :在設置了頁面上下文父標簽開始標記中編碼的屬性后調用

???? public ? int ?doStartTag()? throws ?javax.servlet.jsp.JspException?

。返回碼表明JSP實現servlet是否就評估標簽體。


4 :當遇到結否標記時調用

???? public ? int ?doEndTag()? throws ?javax.servlet.jsp.JspException

。返回碼表明JSP是否就繼紐頁面的其余部份。


5 :確保在頁面退出前被調用

????public?void?release()

,釋放資源并重置標簽處理器狀態。


TagSupport類的方法


1 :為所需的父標簽處理器查找運行時標簽棧。一個標簽處理器可以提供其范圍內子標簽調用的方法 :

public?static?final?javax.servlet.jsp.tagext.Tag?findAncestorWithClass(javax.servlet.jsp.tagext.Tag?tag,?java.lang.Class?aClass)

2 :保存和檢索在id屬性中指定的名字 :

????public?void?setId(java.lang.String?string)?

3 :在本地哈希表中設置指定名字的值 :

public?void?setValue(java.lang.String?string,?java.lang.Object?object)

4 :從本地哈希表中獲取指定名稱的值 :

public?java.lang.Object?getValue(java.lang.String?string)

5 :從本地哈希表中刪除指定名稱的值 :

public?void?removeValue(java.lang.String?string)

6 :返回哈希表中關鍵字的一個枚舉 :

public?java.util.Enumeration?getValues()

?

3. 標簽處理器的生命期

???前面的內容? < br >
???
< mytag:helloworld ></ mytag:helloworld >
???后面的內容

為例 :



a.生成servlet需要創建標簽處理器類的一個實例。實現方式通常是調用JSP容器工廠類的一個方法,
工廠類 包含一個標簽處理器實例池 以使其可重用 不再處于激活狀態的對象

b.初始化標簽處理器,使servlet獲知其存在性。servlet通過調用標簽處理器的兩個方法實現此過程:

?? public?void?setPageContext(javax.servlet.jsp.PageContext?pageContext)

public?void?setParent(javax.servlet.jsp.tagext.Tag?tag)

c.如果標簽具有屬性,屬性的取值通過處理器提供setter方法傳入到對象。屬性setter方法是一個標簽支持屬性所需的唯一方法。

d.頁面的上下文和父標簽已被調置,并已具備屬性。此時調用標簽處理器的doStartTag()方法,該方法可以讀取這些變量并執行實現標答功能所需的計算和操作。doStartTag()方法必須返回一個整型數。返回EVAL_BODY_INCLUDE則正常處理標簽體,返回SKIP_BODY則忽略標簽體

e.標簽體被評估或忽視后調用標簽處理器的doEndTag()方法,返回EVAL_PAGE則頁面的其余部分被評估,返回SKIP_PAGE則servlet代碼立即從_jspService()中返回。




4.體標簽處理器的接口與實現

javax.servlet.jsp.tagext.BodyTag是Tag的子接口。

javax.servlet.jsp.tagext.BodyTagSupport是實現BodyTag類。




BodyTagSupport 有一個這樣的方法 :

?public?void?setBodyContent(javax.servlet.jsp.tagext.BodyContent?bodyContent)?


BodyContent是javax.servlet.jsp.JspWriter的子類,但與其父類有所區別。

public?abstract? class ?BodyContent?extends?javax.servlet.jsp.JspWriter



BodyContent對象的內容不自動寫了入servlet的輸出流,而是積累在一字符串緩存中。當標簽體完成后 其對象 仍可在doEndTag()方法中可以應用,由getString()或getReader()方法操作。并在必要時修改及寫入恢復的JspWriter輸出流。

BodyContent類的方法

??public?void?flush()?throws?java.io.IOException

復寫JspWrite.flush()方法以便它總是產生溢出。

public?void?clearBody()

重置BodyContent緩存為空。

????public?abstract?java.io.Reader?getReader();

返回Reader讀取體內容。

????public?abstract?java.lang.String?getString();

返回包含體內容的一個字符串。

????public?abstract?void?writeOut(java.io.Writer?writer)?throws?java.io.IOException;

將體內容寫入指定輸出。

?public?javax.servlet.jsp.JspWriter?getEnclosingWriter()

返回棧中下一個更高的寫入者對象(可能是另一個BodyContent對象)。


BodyTag接口定義了一個新的整型常量



EVAL_BODY_TAG當doStartTag()返回時,使得新的BodyContent對象被創建并與此標簽處理器相關聯。當doAfterBody()返回時,使得JSPservlet在修改完此標簽控制的任意變量后再次評估體。

BodyTag接口的方法

void?setBodyContent(javax.servlet.jsp.tagext.BodyContent?bodyContent);

在當前JspWriter已被寫入,一個新的BodyContent在被創建后由Jspservlet調用,它發生在doStartTag()之后。

?void?doInitBody()?throws?javax.servlet.jsp.JspException;

setBodyContent()之后,體被評估前調用的生命期方法。如果多次評估體,此方法只調用一次。

?public?int?doAfterBody()?throws?javax.servlet.jsp.JspException?

體被評估后,BodyContent寫入者仍處于激活狀態時調用的生命期方法。此方法必須返回EVAL_BODY_TAG或SKIP_BODY,若返回EVAL_BODY_TAG時體再次被評估。

BodyTagSupport類的方法

??public?int?doStartTag()?throws?javax.servlet.jsp.JspException?

復寫TagSupport中的doStartTag()方法。

?public?int?doEndTag()?throws?javax.servlet.jsp.JspException

調用TagSupport中的doEndTag()方法,返回結果。

public?void?setBodyContent(javax.servlet.jsp.tagext.BodyContent?bodyContent)

在一保護成員變量bodyContent中保存新的體內容對象,子類可直接訪問此對象。

public?void?doInitBody()?throws?javax.servlet.jsp.JspException?

缺省什么都不做。被需要執行初始化的子類所復寫。

public?int?doAfterBody()?throws?javax.servlet.jsp.JspException

每次體被評估后由JSPservlet調用,體內容對象仍處于激活狀態。返回SKEP_BODY或EVAL_BODY_TAG則體再次被評估

?public?void?release()?

設置bodyContent對象為null,然后調用super.release()。

public?javax.servlet.jsp.tagext.BodyContent?getBodyContent()

返回bodyContent變量。子類已經可以訪問保護變量,但此方法允許無關的標簽處理類對此體內容發送輸出。

public?javax.servlet.jsp.JspWriter?getPreviousOut()?

在bodyContent變量上調用getEnclosingWriter()并返回結果的簡便方法。

5.體標簽處理器的生命期

a.生成servlet需要創建標簽處理器類的一個實例。實現方式通常是調用JSP容器的工廠類的一個方法,工廠類包含一個標簽處理器實例池以使其可重用不再處于激活狀態的對象。

b.初始化標簽處理器,使servlet獲知其存在性。servlet通過調用標簽處理器的兩個方法實現此過程:setPageContext(PageContextctx)和setParent(Tagparent)。

c.如果標簽具有屬性,屬性的取值通過處理器提供setter方法傳入到對象。屬性setter方法是一個標簽支持屬性所需的唯一方法。

d.頁面的上下文和父標簽已被調置,并已具備屬性。調用標簽處理器的doStartTag()方法,該方法可以讀取這些變量并執行實現標答功能所需的計算和操作。

doStartTag()方法必須返回一個整型數。

返回EVAL_BODY_TAG則正常處理標簽體(跳到e);

返回SKIP_BODY則從初始JSP頁面中直到此標簽結束標記處的內容均被忽略。(跳到f)

e.如果返回EVAL_BODY_TAG時,則正常處理標簽體。

e1.在棧中保存當前的JspWriter對象,創建新的BodyContent對象,并將其置為JSP頁面的out對象保存在上下文范圍內名為name的屬性中。并調用它的setBodyContent()方法。

e2.調用doInitBody()方法進行初始化。

e3.處理標簽體。將輸出寫入BodyContent對象中,此過程依賴于TLD的標簽元素,有三種可能取值。

e4.調用doAfterBody()方法,將體內體內容寫入JspWriter,可如下實現:

JspWriterout = bodyContent.getEnclosingWriter();
out.println(bodyContent.getString());
// bodyContent.writeOut(out);
bodyContent.clear();

e5.doAfterBody()方法返回兩種可能:

返回EVAL_BODY_TAG時,再對標簽體進行評估,這是數組和枚舉被循環處理的典型情況。

返回SKIP_PAGE時,繼續頁面的其余部份。

e6.體內容完成,因此創建它的過程被反向:

調用pageContent.popBody()方法檢索前面的JspWriter對象。

將寫入者設置回out隱含對象。

f.標簽體被評估或忽視后調用doEndTag()方法,允許標簽處理器像輸出流發回內容。

返回EVAL_PAGE則頁面的其余部分被評估;

返回SKIP_PAGE則servlet代碼立即從_jspService()中返回。

g.此時體的內容在受保護的bodyContent對象中仍然可用。

可以將它寫入servlet輸出流中:

JspWriterout=pageContext.getOut();

out.println(bodyContent.getString());

或者

bodyContent.WriteOut(pageContext.getOut());


6.標簽附加信息類


四、標簽指令

taglib指令元素的目的是指定TLD的位置,設置在頁面上與標簽區分開來的一個短別名。

語法:

<% @taglib?uri = " /helloworld " ?prefix = " mytag " %>

屬性:prefix:用于標識標簽庫的唯一標識。uri:標簽庫本身的URI。

uri不必指向一個實際文件,它是JSP容器可以在web.xml中查找實際文件位置的唯一標識符。