http://dev.csdn.net/article/48/48512.shtm
帶屬性的標(biāo)簽
在標(biāo)簽handler中定義屬性
對于每一個(gè)標(biāo)簽屬性,都必須在標(biāo)簽handler中定義一個(gè)屬性以及符合JavaBean結(jié)構(gòu)規(guī)范的get和set方法。例如,logic:present標(biāo)簽的標(biāo)簽handler
protected String parameter = null;
public String getParameter() {
return (this.parameter);
}
public void setParameter(String parameter) {
this.parameter = parameter;
}
注意,如果屬性命名為id并且標(biāo)簽handler繼承自TagSupport類,那么就不需要定義屬性和set和get方法,因?yàn)樗鼈円呀?jīng)由TagSupport定義了。
值為String的標(biāo)簽屬性可以指定標(biāo)簽handler可用的隱式對象的一個(gè)屬性。通過向隱式對象的[set|get]Attribute方法傳遞標(biāo)簽屬性值可以訪問一個(gè)隱式對象屬性。這是將腳本變量名傳遞給標(biāo)簽handler的好方式,在這里腳本變量與儲(chǔ)存在頁面上下文中的對象相關(guān)聯(lián)(見隱式對象)。
attribute元素
對于每一個(gè)標(biāo)簽屬性,都必須在attribute元素中指定這個(gè)屬性是否是必需的、其值是否可以由表達(dá)式確定、還可能指定屬性的類型。對于靜態(tài)值,類型總是java.lang.String。如果rtexprvalue元素是true或者yes,那么type元素定義會(huì)將任何指定的表達(dá)式的預(yù)期返回類型指定為屬性的值。
<attribute>
<name>attr1</name>
<required>true|false|yes|no</required>
<rtexprvalue>true|false|yes|no</rtexprvalue>
<type>fully_qualified_type</type>
</attribute>
如果tag屬性不是必需的,那么標(biāo)簽handler應(yīng)該提供一個(gè)默認(rèn)值。
logic:present標(biāo)簽的tag元素聲明parameter屬性不是必需的(因?yàn)闃?biāo)簽還可以測試是否存在其它實(shí)體,如bean屬性)以及其值可以由運(yùn)行時(shí)表達(dá)式設(shè)置。
<tag>
<name>present</name>
<tag-class>org.apache.struts.taglib.
logic.PresentTag</tag-class>
<body-content>JSP</body-content>
...
<attribute>
<name>parameter</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
...
</tag>
屬性驗(yàn)證
標(biāo)簽庫的文檔應(yīng)該描述標(biāo)簽屬性的有效值。在轉(zhuǎn)換JSP頁面時(shí),Web容器將強(qiáng)制應(yīng)用每一個(gè)屬性的TLD元素中包含的限制。
在轉(zhuǎn)換時(shí)還用從TagExtraInfo派生的類的isValid方法驗(yàn)證傳遞給標(biāo)簽的屬性。這個(gè)類也用于提供有關(guān)標(biāo)簽定義的腳本變量的信息(見提供有關(guān)腳本變量的信息)。
用TagData對象向isValid方法傳遞屬性信息,它包含每一個(gè)標(biāo)簽屬性的屬性-值元組。因?yàn)轵?yàn)證在轉(zhuǎn)換時(shí)發(fā)生,所以在請求時(shí)計(jì)算的屬性值將設(shè)置為TagData.REQUEST_TIME_VALUE。
<tt:twa attr1="value1"/>標(biāo)簽有下列TLD attribute元素:
<attribute>
<name>attr1</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
這個(gè)聲明表明attr1的值可以在運(yùn)行時(shí)確定。
下面的isValid方法檢查attr1的值是否為有效的布爾值。注意由于的attr1值可以在運(yùn)行時(shí)計(jì)算,所以isValid必須檢查標(biāo)簽用戶是否選擇了提供運(yùn)行時(shí)值。
public class TwaTEI extends TagExtraInfo {
public boolean isValid(Tagdata data) {
Object o = data.getAttribute("attr1");
if (o != null && o != TagData.REQUEST_TIME_VALUE) {
if (((String)o).toLowerCase().equals("true") ||
((String)o).toLowerCase().equals("false") )
return true;
else
return false;
}
else
return true; }
}
帶正文的標(biāo)簽
標(biāo)簽handler
帶正文的標(biāo)簽的標(biāo)簽handler根據(jù)標(biāo)簽handler是否需要與正文交互而有不同的實(shí)現(xiàn)。我們說的交互的意思是標(biāo)簽handler讀取或者修改正文的內(nèi)容。
標(biāo)簽handler不與正文交互
如果標(biāo)簽handler不需要與正文交互,那么標(biāo)簽handler應(yīng)該實(shí)現(xiàn)Tag接口(或者從TagSupport派生)。如果需要對標(biāo)簽的正文進(jìn)行判斷,那么doStartTag方法就需要返回EVAL_BODY_INCLUDE,否則,它應(yīng)該返回SKIP_BODY。
如果標(biāo)簽handler需要反復(fù)地判斷正文,那么它就應(yīng)該實(shí)現(xiàn)IterationTag接口或者從TagSupport派生。如果它確定需要再次評估正文,那么它應(yīng)該從doStartTag和doAfterBody方法返回EVAL_BODY_AGAIN。
標(biāo)簽handler與正文交互
如果標(biāo)簽handler需要與正文交互,那么標(biāo)簽handler必須實(shí)現(xiàn)BodyTag (或者從BodyTagSupport派生)。這種處理器通常實(shí)現(xiàn)doInitBody和doAfterBody方法。這些方法與由JSP頁面的servlet傳遞給tag handler的正文內(nèi)容交互。
正文內(nèi)容支持幾種讀取和寫入其內(nèi)容的方法。標(biāo)簽handler可以用正文內(nèi)容的getString或者getReader方法從正文中提取信息,用writeOut(out)方法將正文內(nèi)容寫入一個(gè)輸出流。為writeOut方法提供的writer是用標(biāo)簽handler的getPreviousOut方法得到的。用這個(gè)方法保證標(biāo)簽handler的結(jié)果對于其外圍標(biāo)簽handler是可用的。
如果需要對標(biāo)簽的正文進(jìn)行判斷,那么doStartTag方法需要返回EVAL_BODY_BUFFERED,否則它就應(yīng)該返回SKIP_BODY。
在已經(jīng)設(shè)置正文內(nèi)容之后、但是對它進(jìn)行判斷之前調(diào)用doInitBody方法。一般用這個(gè)方法執(zhí)行所有依賴于正文內(nèi)容的初始化。
doAfterBody方法在判斷了正文內(nèi)容之后調(diào)用。
像doStartTag方法一樣,doAfterBody必須返回指明是否繼續(xù)判斷正文的指示。因此,如果應(yīng)該再次判斷正文,就像實(shí)現(xiàn)枚舉標(biāo)簽的情況,那么doAfterBody應(yīng)該返回EVAL_BODY_BUFFERED,否則doAfterBody應(yīng)該返回SKIP_BODY。
標(biāo)簽handler應(yīng)該在release方法中重新設(shè)置其狀態(tài)并釋放所有私有資源。
下面的例子讀取正文的內(nèi)容(它包含一個(gè)SQL查詢)并將它傳遞給一個(gè)執(zhí)行這個(gè)查詢的對象。因?yàn)椴恍枰獙φ脑俅闻袛啵詃oAfterBody返回SKIP_BODY。
public class QueryTag extends BodyTagSupport {
public int doAfterBody()
throws JspTagException {
BodyContent bc = getBodyContent();
// get the bc as string
String query = bc.getString();
// clean up bc.clearBody();
try {
Statement stmt = connection.createStatement();
result = stmt.executeQuery(query);
} catch (SQLException e) {
throw new JspTagException("QueryTag: " +
e.getMessage());
}
return SKIP_BODY;
}
}
body-content元素
對于有正文的標(biāo)簽,必須用body-content元素指定正文內(nèi)容的類型:
正文內(nèi)容包含自定義和核心標(biāo)簽、腳本元素以及屬于JSP的HTML文字。這是為Struts logic:present標(biāo)簽聲明的值。所有其它類型的正文內(nèi)容——如傳遞給查詢標(biāo)簽的SQL語句,都標(biāo)記為tagdependent。
注意body-content元素的值不影響標(biāo)簽handler對正文的解讀,這個(gè)元素只是由編寫工具用于呈現(xiàn)正文內(nèi)容。
定義腳本變量的標(biāo)簽
標(biāo)簽handler
標(biāo)簽handler負(fù)責(zé)創(chuàng)建腳本變量引用的對象并設(shè)置到頁面可以訪問的上下文中。它是用pageContext.setAttribute(name, value, scope)或者pageContext.setAttribute(name, value)方法完成這項(xiàng)工作的。通常傳遞給自定義標(biāo)簽的屬性指定腳本變量對象的名字,通過調(diào)用在使用范圍對象中描述的屬性的get方法可以提取這個(gè)名字。
如果腳本變量的值依賴于在標(biāo)簽handler上下文中出現(xiàn)的一個(gè)對象,那么它可以用pageContext.getAttribute(name, scope)方法提取這個(gè)對象。
一般的通常過程是標(biāo)簽handler提取腳本變量、對對象執(zhí)行一些處理、再用pageContext.setAttribute(name, object)方法設(shè)置腳本變量的值。
表16-4總結(jié)了對象可以有的作用域。作用域限制了對象的可訪問性和壽命。
提供有關(guān)腳本變量的信息
在定義腳本變量的標(biāo)簽中描述的例子定義了用于訪問圖書信息的腳本變量:book
<bean:define id="book"
name="bookDB" property="bookDetails"
type="database.BookDetails"/>
<font color="red" size="+2">
<%=messages.getString("CartRemoved")%>
<strong><jsp:getProperty name="book"
property="title"/></strong>
<br> <br>
</font>
在轉(zhuǎn)換包含這個(gè)標(biāo)簽的JSP頁面時(shí),Web容器會(huì)生成同步腳本變量與由變量引用的對象的代碼。要生成這些代碼,Web容器需要關(guān)于腳本變量的一些信息:
有兩種方法提供這種信息:指定variable TLD子元素或者定義tag extra info類并在TLD中包含tei-class元素。用variable元素更簡單,但是靈活性要差一些。
variable 元素
· name-from-attribute:一個(gè)屬性的名字,其轉(zhuǎn)換時(shí)(translation-time)值將給出屬性的名字
必須有name-given或者name-from-attribute之中的一個(gè)。下列子元素是可選的:
· variable-class—變量的完全限定名。默認(rèn)為java.lang.String。
· declare—變量是否引用新對象。默認(rèn)為True。
· scope—定義的腳本變量的作用域。默認(rèn)為NESTED。表16-5描述了腳本變量的可用性以及必須設(shè)置或者重新設(shè)置變量值的方法。
在實(shí)現(xiàn)BodyTag的標(biāo)簽handler的doInitBody 和doAfterBody方法中,否則,在 doStartTag中 | ||
在實(shí)現(xiàn)BodyTag的標(biāo)簽handler的doInitBody 和doAfterBody方法中,否則,在 doStartTag和doEndTag中 | ||
Struts bean:define標(biāo)簽的實(shí)現(xiàn)符合JSP規(guī)范版本1.1,它要求定義tag extra info類。JSP規(guī)范版本1.2增加了variable元素。可以為bean:define標(biāo)簽定義下面的variable元素:
<tag>
<variable>
<name-from-attribute>id</name-from-attribute>
<variable-class>database.BookDetails</variable-class>
<declare>true</declare>
<scope>AT_BEGIN</scope>
</variable>
</tag>
TagExtraInfo類
通過擴(kuò)展類javax.servlet.jsp.TagExtraInfo定義tag extra info類。TagExtraInfo. A TagExtraInfo必須實(shí)現(xiàn)getVariableInfo方法以返回包含下列信息的VariableInfo對象數(shù)組:
Web容器向getVariableInfo方法傳遞包含每一個(gè)標(biāo)簽屬性的屬性-值元組的名為data的參數(shù)。這些屬性可以用于為VariableInfo對象提供腳本變量名和類。
Struts標(biāo)簽庫提供有關(guān)由DefineTei tag extra info類中的bean:define標(biāo)簽創(chuàng)建的腳本變量的信息。由于腳本變量的name (book)和class (database.BookDetails)作為標(biāo)簽屬性傳遞,所以可以用data.getAttributeString方法提取它們,并用于填充VariableInfo構(gòu)造函數(shù)。要使腳本變量book用于頁面的其他地方,book的作用域設(shè)置為AT_BEGIN。
public class DefineTei extends TagExtraInfo {
public VariableInfo[] getVariableInfo(TagData data) {
String type = data.getAttributeString("type");
if (type == null)
type = "java.lang.Object";
return new VariableInfo[] {
new VariableInfo(data.getAttributeString("id"),
type,
true,
VariableInfo.AT_BEGIN)
};
}
}
為腳本變量定義的tag extra info類的完全限定名必須在tag元素的tei-class子元素的TLD中聲明。因此,DefineTei的tei-class元素像下面這樣:
<tei-class>
org.apache.struts.taglib.bean.DefineTagTei
</tei-class>
標(biāo)簽協(xié)同操作
標(biāo)簽通過共享對象實(shí)現(xiàn)合作。JSP技術(shù)支持兩種類型的對象共享。
第一種類型要求在頁面上下文中命名和儲(chǔ)存共享的對象(JSP頁面和標(biāo)簽handler都可以訪問的一種隱式對象)。要訪問由另一個(gè)標(biāo)簽創(chuàng)建和命名的對象,標(biāo)簽handler使用pageContext.getAttribute(name, scope)方法。
在第二種對象共享類型中,由一組嵌入標(biāo)簽中的外圍標(biāo)簽handler創(chuàng)建的對象可以被所有內(nèi)部標(biāo)簽handler訪問。這種形式的對象共享的優(yōu)點(diǎn)是它對對象使用私有命名空間,因此減少了潛在的命名沖突。
要訪問由外圍標(biāo)簽創(chuàng)建的對象,標(biāo)簽handler必須首先用靜態(tài)方法TagSupport.findAncestorWithClass(from, class)或者TagSupport.getParent方法獲得其外圍標(biāo)簽。在不能保證有特定的嵌入標(biāo)簽handler時(shí)應(yīng)該使用前一個(gè)方法。一旦獲取了上級,那么標(biāo)簽handler就可以訪問所有靜態(tài)或動(dòng)態(tài)創(chuàng)建的對象了。靜態(tài)創(chuàng)建的對象是父標(biāo)簽的成員。私有對象也可以動(dòng)態(tài)創(chuàng)建。這種對象可以用setValue方法儲(chǔ)存在標(biāo)簽 handler中,并用getValue方法獲取它。
下面的例子展示了同時(shí)支持命名的和私有對象方式共享對象的標(biāo)簽handler。在這個(gè)例子中,查詢標(biāo)簽的handler檢查名為connection的屬性是否已在doStartTag方法中設(shè)置。如果屬性已經(jīng)設(shè)置,那么handler就從頁面上下文中獲取連接對象。否則,標(biāo)簽handler首先獲取外圍標(biāo)簽的標(biāo)簽handler,然后從那個(gè)handler中獲取連接對象。
public class QueryTag extends BodyTagSupport {
private String connectionId;
public int doStartTag()
throws JspException {
String cid = getConnection();
if (cid != null) {
// there is a connection id, use it
connection =(Connection)pageContext.
getAttribute(cid);
} else {
ConnectionTag ancestorTag =
(ConnectionTag)findAncestorWithClass(this,
ConnectionTag.class);
if (ancestorTag == null) {
throw new JspTagException("A query without
a connection attribute must be nested
within a connection tag.");
}
connection = ancestorTag.getConnection();
}
}
}
由這個(gè)標(biāo)簽 handler實(shí)現(xiàn)的查詢標(biāo)簽可以以下面任何一種方式使用:
<tt:connection id="con01" ....>
...
</tt:connection>
<tt:query id="balances" connection="con01">
SELECT account, balance FROM acct_table
where customer_number = <%= request.getCustno()%>
</tt:query>
<tt:connection ...>
<x:query id="balances">
SELECT account, balance FROM acct_table
where customer_number = <%= request.getCustno()%>
</x:query>
</tt:connection>
標(biāo)簽handler的TLD必須用下面聲明指明connection屬性是可選的:
<tag>
...
<attribute>
<name>connection</name>
<required>false</required>
</attribute>
</tag>
示例
本節(jié)中描述的自定義標(biāo)簽展示了在開發(fā)JSP應(yīng)用程序時(shí)會(huì)經(jīng)常遇到的兩個(gè)問題的解決方法:盡可能減少JSP頁面中的Java編程以及保證整個(gè)應(yīng)用程序的共同外觀。在這個(gè)過程中,展示了本章前面討論過的許多類型的標(biāo)簽。
迭代(Iteration)標(biāo)簽
構(gòu)建依賴于動(dòng)態(tài)生成的數(shù)據(jù)的頁面內(nèi)容通常需要使用流控制腳本語句。通過將流控制邏輯轉(zhuǎn)換到標(biāo)簽handler中,流控制標(biāo)簽減少了在JSP頁面中需要的腳本量。
Struts logic:iterate標(biāo)簽從儲(chǔ)存在JavaBeans組件中的集合中獲取對象并將它們指定給腳本變量。標(biāo)簽的正文從腳本變量中提取信息。如果集合中仍有元素,則iterate標(biāo)簽會(huì)再次對正文進(jìn)行判斷。
JSP頁面
兩個(gè)Duke's Bookstore應(yīng)用程序頁面catalog.jsp和showcart.jsp使用了logic:iterate標(biāo)簽以迭代對象的集合。下面展示了catalog.jsp的一部分。JSP頁面用bookDB bean集合(由property屬性命名)初始化iterate標(biāo)簽。iterate標(biāo)簽在對集合上的每一次迭代中設(shè)置book腳本變量。book變量的bookId屬性作為另一個(gè)腳本變量公開。兩個(gè)變量的屬性都用于動(dòng)態(tài)生成一個(gè)包含到其他頁面的圖書目錄信息的鏈接的表。
<logic:iterate name="bookDB" property="books"
id="book" type="database.BookDetails">
<bean:define id="bookId" name="book" property="bookId"
type="java.lang.String"/><tr>
<td bgcolor="#ffffaa">
<a href="<%=request.getContextPath()%>
/bookdetails?bookId=<%=bookId%>">
<strong><jsp:getProperty name="book"
property="title"/> </strong></a></td><td bgcolor="#ffffaa" rowspan=2>
<jsp:setProperty name="currency" property="amount"
value="<%=book.getPrice()%>"/>
<jsp:getProperty name="currency" property="format"/>
</td> <td bgcolor="#ffffaa" rowspan=2>
<a href="<%=request.getContextPath()%>
/catalog?Add=<%=bookId%>">
<%=messages.getString("CartAdd")%>
</a></td></tr><tr>
<td bgcolor="#ffffff">
<%=messages.getString("By")%> <em>
<jsp:getProperty name="book"
property="firstName"/>
<jsp:getProperty name="book"
property="surname"/> </em> </td> </tr>
</logic:iterate>
標(biāo)簽handler
Struts logic:iterate標(biāo)簽的實(shí)現(xiàn)符合JSP版本1.1規(guī)范的要求,它需要擴(kuò)展BodyTagSupport類。JSP版本1.2規(guī)范添加了簡化迭代性地對正文判斷的編程標(biāo)簽的功能(在不與正文交互的標(biāo)簽handler中描述)。下面的討論是使用這些功能的實(shí)現(xiàn)。
logic:iterate標(biāo)簽以幾種方式支持集合初始化:用作為標(biāo)簽屬性而提供的集合,或者用作為bean或者bean屬性集合而提供的集合。我們的例子使用后一種方法。doStartTag中的大多數(shù)代碼是關(guān)于構(gòu)建對于一個(gè)對象集合的迭代器的。方法首先檢查是否設(shè)置了handler的集合屬性,如果沒有,則進(jìn)一步檢查bean和property屬性。如果name和property屬性都設(shè)置了,則doStartTag調(diào)用使用JavaBean自省方法的工具方法來獲取集合。一旦確定了集合對象,方法就構(gòu)建迭代器。
如果迭代器中還有元素,那么doStartTag就設(shè)置腳本變量的值為下一個(gè)元素,然后表明要對這個(gè)正文進(jìn)行判斷,否則就返回SKIP_BODY結(jié)束迭代。
在判斷完正文后,doAfterBody方法提取正文內(nèi)容并將它寫入輸出流。然后清除正文內(nèi)容對象以便為另一次正文判斷作準(zhǔn)備。如果迭代器還包含元素,那么doAfterBody就再次設(shè)置腳本變量的值為下一個(gè)元素并返回EVAL_BODY_AGAIN以表明應(yīng)該再次對正文進(jìn)行判斷。這樣會(huì)再次執(zhí)行doAfterBody。如果沒有剩余元素了,那么就返回SKIP_BODY終止這個(gè)過程。
public class IterateTag extends TagSupport {
protected Iterator iterator = null;
protected Object collection = null;
protected String id = null;
protected String name = null;
protected String property = null;
protected String type = null;
public int doStartTag() throws JspException {
Object collection = this.collection;
if (collection == null) {
try {
Object bean = pageContext.findAttribute(name);
if (bean == null) {
... throw an exception
}
if (property == null)
collection = bean;
else
collection =
PropertyUtils.
getProperty(bean, property);
if (collection == null) {
... throw an exception
}
} catch
... catch exceptions thrown
by PropertyUtils.getProperty
}
}
// Construct an iterator for this collection
if (collection instanceof Collection)
iterator = ((Collection) collection).iterator();
else if (collection instanceof Iterator)
iterator = (Iterator) collection;
...
}
// Store the first value and evaluate,
// or skip the body if none
if (iterator.hasNext()) {
Object element = iterator.next();
pageContext.setAttribute(id, element);
return (EVAL_BODY_AGAIN);
} else
return (SKIP_BODY);
}
public int doAfterBody() throws JspException {
if (bodyContent != null) {
try {
JspWriter out = getPreviousOut();
out.print(bodyContent.getString());
bodyContent.clearBody();
} catch (IOException e) {
...
}
}
if (iterator.hasNext()) {
Object element = iterator.next();
pageContext.setAttribute(id, element);
return (EVAL_BODY_AGAIN);
} else
return (SKIP_BODY);
}
}
}
標(biāo)簽額外信息類
有關(guān)腳本變量的信息是在IterateTei標(biāo)簽額外信息類中提供的。腳本變量的名字和類以標(biāo)簽屬性的形式傳入,并用于加入VariableInfo構(gòu)造函數(shù)。
public class IterateTei extends TagExtraInfo {
public VariableInfo[] getVariableInfo(TagData data) {
String type = data.getAttributeString("type");
if (type == null)
type = "java.lang.Object";return new VariableInfo[] {
new VariableInfo(data.getAttributeString("id"),
type,
true,
VariableInfo.AT_BEGIN) };
}
}
模板標(biāo)簽庫
模板提供了一種將應(yīng)用程序中每一屏幕都會(huì)出現(xiàn)的共用元素與每一屏幕都會(huì)改變的元素分離開來的方法。將所有公共元素一起放到一個(gè)文件中更容易進(jìn)行維護(hù),并可以加強(qiáng)所有屏幕的外觀一致性。它還使每一屏幕的開發(fā)更容易了,因?yàn)殚_發(fā)者只要注重于該屏幕特定的那部分內(nèi)容就可以了,模板會(huì)負(fù)責(zé)公共部分。
模板是JSP頁面,在每一屏幕需要改變的地方有占位符。每一個(gè)占位符稱為模板的參數(shù)。例如,一個(gè)簡單的模板可能包含在生成的屏幕頂部的一個(gè)標(biāo)題參數(shù)和JSP頁面的正文參數(shù)以設(shè)定屏幕的定制內(nèi)容。
模板使用嵌入的標(biāo)簽——definition、screen和parameter——定義屏幕定義表并使用標(biāo)簽將屏幕定義插入到特定應(yīng)用程序屏幕。
JSP頁面
下面展示Duke's Bookstore例子的模板template.jsp。這一頁面包括一個(gè)創(chuàng)建屏幕定義、并用insert標(biāo)簽將定義中的參數(shù)插入應(yīng)用程序屏幕的JSP頁面。
<%@ taglib uri="/tutorial-template.tld" prefix="tt" %>
<%@ page errorPage="errorpage.jsp" %>
<%@ include file="screendefinitions.jsp" %><html>
<head>
<title>
<tt:insert definition="bookstore"
parameter="title"/>
</title> </head>
<tt:insert definition="bookstore"
parameter="banner"/>
<tt:insert definition="bookstore"
parameter="body"/>
</body>
</html>
screendefinitions.jsp根據(jù)請求屬性selectedScreen創(chuàng)建屏幕定義:
<tt:definition name="bookstore"
screen="<%= (String)request.
getAttribute(\"selectedScreen\") %>">
<tt:screen id="/enter">
<tt:parameter name="title"
value="Duke's Bookstore" direct="true"/>
<tt:parameter name="banner"
value="/banner.jsp" direct="false"/>
<tt:parameter name="body"
value="/bookstore.jsp" direct="false"/>
</tt:screen>
<tt:screen id="/catalog">
<tt:parameter name="title"
value="<%=messages.getString("TitleBookCatalog")%>"
direct="true"/>
...
</tt:definition>
模板由Dispatcher servlet實(shí)例化。Dispatcher首先得到所請求的屏幕并將它儲(chǔ)存為請求的屬性。這是必要的,因?yàn)樵谙騮emplate.jsp轉(zhuǎn)發(fā)請求時(shí),請求URL不包含原來的請求(如/bookstore3/catalog),而是反映轉(zhuǎn)發(fā)布頁面的路徑(/bookstore3/template.jsp)。最后,servlet將請求分發(fā)給template.jsp:
public class Dispatcher extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response) {
request.setAttribute("selectedScreen",
request.getServletPath());
RequestDispatcher dispatcher =
request.getRequestDispatcher("/template.jsp");
if (dispatcher != null)
dispatcher.forward(request, response);
} public void doPost(HttpServletRequest request,
HttpServletResponse response) {
request.setAttribute("selectedScreen",
request.getServletPath());
RequestDispatcher dispatcher =
request.getRequestDispatcher("/template.jsp");
if (dispatcher != null)
dispatcher.forward(request, response);
}
}
標(biāo)簽handler
模板標(biāo)簽庫包含四個(gè)標(biāo)簽handler——DefinitionTag、ScreenTag、ParameterTag和InsertTag,它們展示了協(xié)同操作標(biāo)簽的使用。DefinitionTag、ScreenTag和ParameterTag組成了一組嵌入的標(biāo)簽handler,它們共享公共(public)和私有(private)對象。DefinitionTag創(chuàng)建由InsertTag使用的名為definition的公共對象,
在doStartTag中,DefinitionTag創(chuàng)建一個(gè)名為screens的公共對象,它包含屏幕定義的一個(gè)哈希表。屏幕定義包含屏幕標(biāo)識(shí)符和一組與該屏幕相關(guān)聯(lián)的參數(shù)。
public int doStartTag() {
HashMap screens = null;
screens = (HashMap) pageContext.getAttribute("screens",
pageContext.APPLICATION_SCOPE);
if (screens == null)
pageContext.setAttribute("screens", new HashMap(),
pageContext.APPLICATION_SCOPE);
return EVAL_BODY_INCLUDE; }
ScreenTag和ParameterTag用作為這些標(biāo)簽屬性提供的文字填充屏幕定義表。
.
在doEndTag中,DefinitionTag創(chuàng)建Definition類的一個(gè)公共對象,根據(jù)在請求中傳遞的URL從screens對象中選擇一個(gè)屏幕定義,并用它初始化Definition對象。
public int doEndTag()throws JspTagException {
try {
Definition definition = new Definition();
HashMap screens = null;
ArrayList params = null;
TagSupport screen = null;
screens = (HashMap)
pageContext.getAttribute("screens",
pageContext.APPLICATION_SCOPE);
if (screens != null)
params = (ArrayList) screens.get(screenId);
else
...
if (params == null)
...
Iterator ir = null;
if (params != null)
ir = params.iterator();
while ((ir != null) && ir.hasNext())
definition.setParam((Parameter) ir.next());
// put the definition in the page context
pageContext.setAttribute(
definitionName, definition);
} catch (Exception ex) {
ex.printStackTrace();
} return EVAL_PAGE;
}
如果在請求中傳遞的URL是/enter,那么Definition包含表16-6中第一行的項(xiàng)目:
InsertTag使用Definition將屏幕定義的參數(shù)插入響應(yīng)中。在doStartTag方法中,它從頁面上下文中獲取定義對象。
public int doStartTag() {
// get the definition from the page context
definition = (Definition) pageContext.
getAttribute(definitionName);
// get the parameter
if (parameterName != null && definition != null)
parameter = (Parameter)definition.
getParam(parameterName);
if (parameter != null)
directInclude = parameter.isDirect();
return SKIP_BODY;
}
doEndTag方法插入?yún)?shù)值。如果參數(shù)是直接的,那么就直接將它插入響應(yīng)中,否則,請求就被發(fā)送給參數(shù),而其響應(yīng)則被動(dòng)態(tài)地包含進(jìn)整個(gè)響應(yīng)中。
public int doEndTag()throws JspTagException {
try {
if (directInclude && parameter != null)
pageContext.getOut().print(parameter.getValue());
else {
if ((parameter != null) &&
(parameter.getValue() != null))
pageContext.include(parameter.getValue());
}
} catch (Exception ex) {
throw new JspTagException(ex.getMessage());
}
return EVAL_PAGE;
}
如何調(diào)用標(biāo)簽handler?
Tag接口定義了標(biāo)簽handler與JSP頁面的servlet之間的基本協(xié)議。它定義了生命周期以及在遇到開始和結(jié)束標(biāo)簽時(shí)要調(diào)用的方法。
JSP頁面的servlet在調(diào)用doStartTag之前調(diào)用setPageContext、setParent和屬性設(shè)置方法。JSP頁面的servlet還保證在結(jié)束頁面之前調(diào)用標(biāo)簽handler的release。
下面是典型的標(biāo)簽handler方法調(diào)用順序:
A Tag t = new ATag();
t.setPageContext(...);
t.setParent(...);
t.setAttribute1(value1);
t.setAttribute2(value2);
t.doStartTag();
t.doEndTag();
t.release();
BodyTag接口通過定義讓標(biāo)簽handler訪問其正文的其他方法擴(kuò)展Tag。這個(gè)接口提供三個(gè)新方法:
· setBodyContent—?jiǎng)?chuàng)建正文內(nèi)容并添加給tag handler
· doInitBody—在評估標(biāo)簽正文之前調(diào)用
· doAfterBody—在評估標(biāo)簽正文之后調(diào)用
t.doStartTag();
out = pageContext.pushBody();
t.setBodyContent(out);
// perform any initialization needed after body content is set t.doInitBody();
t.doAfterBody();
// while doAfterBody returns EVAL_BODY_BUFFERED we
// iterate body evaluation
...
t.doAfterBody();
t.doEndTag();
t.pageContext.popBody();
t.release();