帶屬性的標簽
在標簽handler中定義屬性
對于每一個標簽屬性,都必須在標簽handler中定義一個屬性以及符合JavaBean結構規范的get和set方法。例如,logic:present標簽的標簽handler
<logic:present parameter="Clear">
包含下列聲明和方法:
protected String parameter = null;
public String getParameter() {
return (this.parameter);
}
public void setParameter(String parameter) {
this.parameter = parameter;
}
注意,如果屬性命名為id并且標簽handler繼承自TagSupport類,那么就不需要定義屬性和set和get方法,因為它們已經由TagSupport定義了。
值為String的標簽屬性可以指定標簽handler可用的隱式對象的一個屬性。通過向隱式對象的[set|get]Attribute方法傳遞標簽屬性值可以訪問一個隱式對象屬性。這是將腳本變量名傳遞給標簽handler的好方式,在這里腳本變量與儲存在頁面上下文中的對象相關聯(見隱式對象)。
attribute元素
對于每一個標簽屬性,都必須在attribute元素中指定這個屬性是否是必需的、其值是否可以由表達式確定、還可能指定屬性的類型。對于靜態值,類型總是java.lang.String。如果rtexprvalue元素是true或者yes,那么type元素定義會將任何指定的表達式的預期返回類型指定為屬性的值。
<attribute>
<name>attr1</name>
<required>true|false|yes|no</required>
<rtexprvalue>true|false|yes|no</rtexprvalue>
<type>fully_qualified_type</type>
</attribute>
如果tag屬性不是必需的,那么標簽handler應該提供一個默認值。
logic:present標簽的tag元素聲明parameter屬性不是必需的(因為標簽還可以測試是否存在其它實體,如bean屬性)以及其值可以由運行時表達式設置。
<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>
屬性驗證
標簽庫的文檔應該描述標簽屬性的有效值。在轉換JSP頁面時,Web容器將強制應用每一個屬性的TLD元素中包含的限制。
在轉換時還用從TagExtraInfo派生的類的isValid方法驗證傳遞給標簽的屬性。這個類也用于提供有關標簽定義的腳本變量的信息(見提供有關腳本變量的信息)。
用TagData對象向isValid方法傳遞屬性信息,它包含每一個標簽屬性的屬性-值元組。因為驗證在轉換時發生,所以在請求時計算的屬性值將設置為TagData.REQUEST_TIME_VALUE。
<tt:twa attr1="value1"/>標簽有下列TLD attribute元素:
<attribute>
<name>attr1</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
這個聲明表明attr1的值可以在運行時確定。
下面的isValid方法檢查attr1的值是否為有效的布爾值。注意由于的attr1值可以在運行時計算,所以isValid必須檢查標簽用戶是否選擇了提供運行時值。
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; }
}
帶正文的標簽
標簽handler
帶正文的標簽的標簽handler根據標簽handler是否需要與正文交互而有不同的實現。我們說的交互的意思是標簽handler讀取或者修改正文的內容。
標簽handler不與正文交互
如果標簽handler不需要與正文交互,那么標簽handler應該實現Tag接口(或者從TagSupport派生)。如果需要對標簽的正文進行判斷,那么doStartTag方法就需要返回EVAL_BODY_INCLUDE,否則,它應該返回SKIP_BODY。
如果標簽handler需要反復地判斷正文,那么它就應該實現IterationTag接口或者從TagSupport派生。如果它確定需要再次評估正文,那么它應該從doStartTag和doAfterBody方法返回EVAL_BODY_AGAIN。
標簽handler與正文交互
如果標簽handler需要與正文交互,那么標簽handler必須實現BodyTag (或者從BodyTagSupport派生)。這種處理器通常實現doInitBody和doAfterBody方法。這些方法與由JSP頁面的servlet傳遞給tag handler的正文內容交互。
正文內容支持幾種讀取和寫入其內容的方法。標簽handler可以用正文內容的getString或者getReader方法從正文中提取信息,用writeOut(out)方法將正文內容寫入一個輸出流。為writeOut方法提供的writer是用標簽handler的getPreviousOut方法得到的。用這個方法保證標簽handler的結果對于其外圍標簽handler是可用的。
如果需要對標簽的正文進行判斷,那么doStartTag方法需要返回EVAL_BODY_BUFFERED,否則它就應該返回SKIP_BODY。
doInitBody 方法
在已經設置正文內容之后、但是對它進行判斷之前調用doInitBody方法。一般用這個方法執行所有依賴于正文內容的初始化。
doAfterBody方法
doAfterBody方法在判斷了正文內容之后調用。
像doStartTag方法一樣,doAfterBody必須返回指明是否繼續判斷正文的指示。因此,如果應該再次判斷正文,就像實現枚舉標簽的情況,那么doAfterBody應該返回EVAL_BODY_BUFFERED,否則doAfterBody應該返回SKIP_BODY。
Release方法
標簽handler應該在release方法中重新設置其狀態并釋放所有私有資源。
下面的例子讀取正文的內容(它包含一個SQL查詢)并將它傳遞給一個執行這個查詢的對象。因為不需要對正文再次判斷,所以doAfterBody返回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元素
對于有正文的標簽,必須用body-content元素指定正文內容的類型:
<body-content>JSP|tagdependent</body-content>
正文內容包含自定義和核心標簽、腳本元素以及屬于JSP的HTML文字。這是為Struts logic:present標簽聲明的值。所有其它類型的正文內容——如傳遞給查詢標簽的SQL語句,都標記為tagdependent。
注意body-content元素的值不影響標簽handler對正文的解讀,這個元素只是由編寫工具用于呈現正文內容。
定義腳本變量的標簽
標簽handler
標簽handler負責創建腳本變量引用的對象并設置到頁面可以訪問的上下文中。它是用pageContext.setAttribute(name, value, scope)或者pageContext.setAttribute(name, value)方法完成這項工作的。通常傳遞給自定義標簽的屬性指定腳本變量對象的名字,通過調用在使用范圍對象中描述的屬性的get方法可以提取這個名字。
如果腳本變量的值依賴于在標簽handler上下文中出現的一個對象,那么它可以用pageContext.getAttribute(name, scope)方法提取這個對象。
一般的通常過程是標簽handler提取腳本變量、對對象執行一些處理、再用pageContext.setAttribute(name, object)方法設置腳本變量的值。
表16-4總結了對象可以有的作用域。作用域限制了對象的可訪問性和壽命。
表16-4 對象范圍 |
名字 |
可訪問性 |
壽命 |
page |
當前頁面 |
直到響應被返回到用戶或者請求被傳遞給一個新頁面 |
request |
當前頁面及所有包含或者轉發頁面 |
直到響應被返回到用戶 |
session |
當前請求和所有從同一個瀏覽器發出的后續請求(取決于會話壽命) |
用戶會話的壽命 |
application |
同一Web應用程序的當前和所有未來請求 |
應用程序的壽命 |
提供有關腳本變量的信息
在定義腳本變量的標簽中描述的例子定義了用于訪問圖書信息的腳本變量: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>
在轉換包含這個標簽的JSP頁面時,Web容器會生成同步腳本變量與由變量引用的對象的代碼。要生成這些代碼,Web容器需要關于腳本變量的一些信息:
· 變量名
· 變量類
· 變量是否引用新的對象或者是現有對象
· 變量的可用性
有兩種方法提供這種信息:指定variable TLD子元素或者定義tag extra info類并在TLD中包含tei-class元素。用variable元素更簡單,但是靈活性要差一些。
variable 元素
variable元素有下列子元素:
· name-given:變量名為常量
· name-from-attribute:一個屬性的名字,其轉換時(translation-time)值將給出屬性的名字
必須有name-given或者name-from-attribute之中的一個。下列子元素是可選的:
· variable-class—變量的完全限定名。默認為java.lang.String。
· declare—變量是否引用新對象。默認為True。
· scope—定義的腳本變量的作用域。默認為NESTED。表16-5描述了腳本變量的可用性以及必須設置或者重新設置變量值的方法。
表 16-5 腳本變量可用性 |
值 |
可用性 |
方法 |
NESTED |
開始和結束標簽之間 |
在實現BodyTag的標簽handler的doInitBody 和doAfterBody方法中,否則,在 doStartTag中 |
AT_BEGIN |
從開始標簽到頁面的結束 |
在實現BodyTag的標簽handler的doInitBody 和doAfterBody方法中,否則,在 doStartTag和doEndTag中 |
AT_END |
在結束標簽之后直到頁面的結束 |
在doEndTag中 |
Struts bean:define標簽的實現符合JSP規范版本1.1,它要求定義tag extra info類。JSP規范版本1.2增加了variable元素。可以為bean:define標簽定義下面的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類
通過擴展類javax.servlet.jsp.TagExtraInfo定義tag extra info類。TagExtraInfo. A TagExtraInfo必須實現getVariableInfo方法以返回包含下列信息的VariableInfo對象數組:
· 變量名
· 變量類
· 變量是否引用新對象
· 變量可用性
Web容器向getVariableInfo方法傳遞包含每一個標簽屬性的屬性-值元組的名為data的參數。這些屬性可以用于為VariableInfo對象提供腳本變量名和類。
Struts標簽庫提供有關由DefineTei tag extra info類中的bean:define標簽創建的腳本變量的信息。由于腳本變量的name (book)和class (database.BookDetails)作為標簽屬性傳遞,所以可以用data.getAttributeString方法提取它們,并用于填充VariableInfo構造函數。要使腳本變量book用于頁面的其他地方,book的作用域設置為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>
標簽協同操作
標簽通過共享對象實現合作。JSP技術支持兩種類型的對象共享。
第一種類型要求在頁面上下文中命名和儲存共享的對象(JSP頁面和標簽handler都可以訪問的一種隱式對象)。要訪問由另一個標簽創建和命名的對象,標簽handler使用pageContext.getAttribute(name, scope)方法。
在第二種對象共享類型中,由一組嵌入標簽中的外圍標簽handler創建的對象可以被所有內部標簽handler訪問。這種形式的對象共享的優點是它對對象使用私有命名空間,因此減少了潛在的命名沖突。
要訪問由外圍標簽創建的對象,標簽handler必須首先用靜態方法TagSupport.findAncestorWithClass(from, class)或者TagSupport.getParent方法獲得其外圍標簽。在不能保證有特定的嵌入標簽handler時應該使用前一個方法。一旦獲取了上級,那么標簽handler就可以訪問所有靜態或動態創建的對象了。靜態創建的對象是父標簽的成員。私有對象也可以動態創建。這種對象可以用setValue方法儲存在標簽 handler中,并用getValue方法獲取它。
下面的例子展示了同時支持命名的和私有對象方式共享對象的標簽handler。在這個例子中,查詢標簽的handler檢查名為connection的屬性是否已在doStartTag方法中設置。如果屬性已經設置,那么handler就從頁面上下文中獲取連接對象。否則,標簽handler首先獲取外圍標簽的標簽handler,然后從那個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();
}
}
}
由這個標簽 handler實現的查詢標簽可以以下面任何一種方式使用:
<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>
標簽handler的TLD必須用下面聲明指明connection屬性是可選的:
<tag>
...
<attribute>
<name>connection</name>
<required>false</required>
</attribute>
</tag>
示例
本節中描述的自定義標簽展示了在開發JSP應用程序時會經常遇到的兩個問題的解決方法:盡可能減少JSP頁面中的Java編程以及保證整個應用程序的共同外觀。在這個過程中,展示了本章前面討論過的許多類型的標簽。
迭代(Iteration)標簽
構建依賴于動態生成的數據的頁面內容通常需要使用流控制腳本語句。通過將流控制邏輯轉換到標簽handler中,流控制標簽減少了在JSP頁面中需要的腳本量。
Struts logic:iterate標簽從儲存在JavaBeans組件中的集合中獲取對象并將它們指定給腳本變量。標簽的正文從腳本變量中提取信息。如果集合中仍有元素,則iterate標簽會再次對正文進行判斷。
JSP頁面
兩個Duke's Bookstore應用程序頁面catalog.jsp和showcart.jsp使用了logic:iterate標簽以迭代對象的集合。下面展示了catalog.jsp的一部分。JSP頁面用bookDB bean集合(由property屬性命名)初始化iterate標簽。iterate標簽在對集合上的每一次迭代中設置book腳本變量。book變量的bookId屬性作為另一個腳本變量公開。兩個變量的屬性都用于動態生成一個包含到其他頁面的圖書目錄信息的鏈接的表。
<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>
標簽handler
Struts logic:iterate標簽的實現符合JSP版本1.1規范的要求,它需要擴展BodyTagSupport類。JSP版本1.2規范添加了簡化迭代性地對正文判斷的編程標簽的功能(在不與正文交互的標簽handler中描述)。下面的討論是使用這些功能的實現。
logic:iterate標簽以幾種方式支持集合初始化:用作為標簽屬性而提供的集合,或者用作為bean或者bean屬性集合而提供的集合。我們的例子使用后一種方法。doStartTag中的大多數代碼是關于構建對于一個對象集合的迭代器的。方法首先檢查是否設置了handler的集合屬性,如果沒有,則進一步檢查bean和property屬性。如果name和property屬性都設置了,則doStartTag調用使用JavaBean自省方法的工具方法來獲取集合。一旦確定了集合對象,方法就構建迭代器。
如果迭代器中還有元素,那么doStartTag就設置腳本變量的值為下一個元素,然后表明要對這個正文進行判斷,否則就返回SKIP_BODY結束迭代。
在判斷完正文后,doAfterBody方法提取正文內容并將它寫入輸出流。然后清除正文內容對象以便為另一次正文判斷作準備。如果迭代器還包含元素,那么doAfterBody就再次設置腳本變量的值為下一個元素并返回EVAL_BODY_AGAIN以表明應該再次對正文進行判斷。這樣會再次執行doAfterBody。如果沒有剩余元素了,那么就返回SKIP_BODY終止這個過程。
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);
}
}
}
標簽額外信息類
有關腳本變量的信息是在IterateTei標簽額外信息類中提供的。腳本變量的名字和類以標簽屬性的形式傳入,并用于加入VariableInfo構造函數。
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) };
}
}
模板標簽庫
模板提供了一種將應用程序中每一屏幕都會出現的共用元素與每一屏幕都會改變的元素分離開來的方法。將所有公共元素一起放到一個文件中更容易進行維護,并可以加強所有屏幕的外觀一致性。它還使每一屏幕的開發更容易了,因為開發者只要注重于該屏幕特定的那部分內容就可以了,模板會負責公共部分。
模板是JSP頁面,在每一屏幕需要改變的地方有占位符。每一個占位符稱為模板的參數。例如,一個簡單的模板可能包含在生成的屏幕頂部的一個標題參數和JSP頁面的正文參數以設定屏幕的定制內容。
模板使用嵌入的標簽——definition、screen和parameter——定義屏幕定義表并使用標簽將屏幕定義插入到特定應用程序屏幕。
JSP頁面
下面展示Duke's Bookstore例子的模板template.jsp。這一頁面包括一個創建屏幕定義、并用insert標簽將定義中的參數插入應用程序屏幕的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根據請求屬性selectedScreen創建屏幕定義:
<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實例化。Dispatcher首先得到所請求的屏幕并將它儲存為請求的屬性。這是必要的,因為在向template.jsp轉發請求時,請求URL不包含原來的請求(如/bookstore3/catalog),而是反映轉發布頁面的路徑(/bookstore3/template.jsp)。最后,servlet將請求分發給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);
}
}
標簽handler
模板標簽庫包含四個標簽handler——DefinitionTag、ScreenTag、ParameterTag和InsertTag,它們展示了協同操作標簽的使用。DefinitionTag、ScreenTag和ParameterTag組成了一組嵌入的標簽handler,它們共享公共(public)和私有(private)對象。DefinitionTag創建由InsertTag使用的名為definition的公共對象,
在doStartTag中,DefinitionTag創建一個名為screens的公共對象,它包含屏幕定義的一個哈希表。屏幕定義包含屏幕標識符和一組與該屏幕相關聯的參數。
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用作為這些標簽屬性提供的文字填充屏幕定義表。
表16-6 屏幕定義 |
屏幕Id |
標題 |
橫幅 |
正文 |
/enter |
Duke's Bookstore |
/banner.jsp |
/bookstore.jsp |
/catalog |
Book Catalog |
/banner.jsp |
/catalog.jsp |
/bookdetails |
Book Description |
/banner.jsp |
/bookdetails.jsp |
/showcart |
Shopping Cart |
/banner.jsp |
/showcart.jsp |
/cashier |
Cashier |
/banner.jsp |
/cashier.jsp |
/receipt |
Receipt |
/banner.jsp |
/receipt.jsp |
.
在doEndTag中,DefinitionTag創建Definition類的一個公共對象,根據在請求中傳遞的URL從screens對象中選擇一個屏幕定義,并用它初始化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中第一行的項目:
標題 |
橫幅 |
正文 |
Duke's Bookstore |
/banner.jsp |
/bookstore.jsp |
URL /enter的定義如
表16-7 URL /enter 的屏幕定義 |
參數名 |
參數值 |
isDirect |
title |
Duke's Bookstore |
true |
banner |
/banner.jsp |
false |
body |
/bookstore.jsp |
false |
InsertTag使用Definition將屏幕定義的參數插入響應中。在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方法插入參數值。如果參數是直接的,那么就直接將它插入響應中,否則,請求就被發送給參數,而其響應則被動態地包含進整個響應中。
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;
}
如何調用標簽handler?
Tag接口定義了標簽handler與JSP頁面的servlet之間的基本協議。它定義了生命周期以及在遇到開始和結束標簽時要調用的方法。
JSP頁面的servlet在調用doStartTag之前調用setPageContext、setParent和屬性設置方法。JSP頁面的servlet還保證在結束頁面之前調用標簽handler的release。
下面是典型的標簽handler方法調用順序:
A Tag t = new ATag();
t.setPageContext(...);
t.setParent(...);
t.setAttribute1(value1);
t.setAttribute2(value2);
t.doStartTag();
t.doEndTag();
t.release();
BodyTag接口通過定義讓標簽handler訪問其正文的其他方法擴展Tag。這個接口提供三個新方法:
· setBodyContent—創建正文內容并添加給tag handler
· doInitBody—在評估標簽正文之前調用
· doAfterBody—在評估標簽正文之后調用
典型的調用順序為:
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();