如果想要在本體執行過後,取得執行的結果並作適當處理該如何進行?例如實作一個〈處理標籤屬性與本體〉的 <f:toUpperCase>
標籤?只是繼承 TagSupport
的話沒辦法達到這個目的!你可以繼承 javax.servlet.jsp.tagext.BodyTagSupport
類別來實作,先來看看其類別架構:
在上圖中多了 BodyTag
介面,其繼承自 IterationTag
介面,新增了 setBodyContent()
與 doInitBody()
兩個方法,而 BodyTagSupport
則繼承自 TagSupport
類別,將 doStartTag()
的預設傳回值改為 EVAL_BODY_BUFFERED
,並針對 BodyTag
介面作了簡單的實作。
在繼承 BodyTagSupport
類別實作自訂標籤時,如果 doStartTag()
傳回了 EVAL_BODY_BUFFERED
,則會呼叫 setBodyContent()
方法而後呼叫 doInitBody()
方法,接著再執行標籤本體,也就是流程將變成以下:
基本上,在使用 BodyTagSupport
實作自訂標籤時,並不需要去重新定義 setBodyContent()
與 doInitBody()
方法,只需要知道這兩個方法執行過後,在 doAfterBody()
或 doEndTag()
方法中,就可以透過 getBodyContent()
取得一個 BodyContent
物件(Writer
的子物件),這個物件中包括本體內容執行後的結果,例如透過 BodyContent
的 getString()
方法,就可以字串的方式傳回執行後的本體內容。
如果要將加工後的本體內容輸出使用者的瀏覽器,通常會在 doEndTag()
中使用 pageContext
的 getOut()
取得 JspWriter
物件,然後利用它來輸出內容至使用者的瀏覽器。如果在 doAfterBody()
中使用 pageContext
的 getOut()
方法所取得的物件,與 getBodyContent()
所取得的其實是相同的物件。如果在 doAfterBody()
中,要取得與 doEndTag()
中透過 pageContext
的 getOut()
取得的 JspWriter
物件,則必須透過 BodyContent
的 getEnclosingWriter()
方法。
原因可以在 JSP 轉譯後的 Servlet 程式碼中找到。如果 doStartTag()
傳回 EVAL_BODY_BUFFERED
,則會使用 PageContext
的 pushBody()
將目前的 JspWriter
置入堆疊中,並傳回一個 BodyContent
物件,而後呼叫 setBodyContent()
並傳入這個 BodyContent
物件,然後呼叫 doInitBody()
方法,而在呼叫 doEndTag()
方法前,如果先前 doStartTag()
傳回 EVAL_BODY_BUFFERED
,則會呼叫 PageContext
的 popBody()
,將原本的 JspWriter
從堆疊中取出。
以下使用 BodyTagSupport
類別來實作出〈處理標籤屬性與本體〉的 <f:toUpperCase>
標籤處理器作為示範:
package cc.openhome;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;
public class ToUpperCaseTag extends BodyTagSupport {
@Override
public int doEndTag() throws JspException {
String upper = this.getBodyContent().getString().toUpperCase();
try {
pageContext.getOut().write(upper);
} catch (IOException ex) {
throw new JspException(ex);
}
return EVAL_PAGE;
}
}
在這邊於 doEndTag()
中透過 getBodyContent()
取得 BodyContent
物件,並呼叫其 getString()
取得執行過後的標籤本體內容,再進行轉字母為大寫的動作。轉換後的本體內容,則透過 pageContext
的 getOut()
取得 JspWriter
進行輸出。
記得在 TLD 檔案中定義標籤:
f.tld
<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.1" 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
web-jsptaglibrary_2_1.xsd">
<tlib-version>1.0</tlib-version>
<short-name>f</short-name>
<uri>https://openhome.cc/jstl/fake</uri>
// 略...
<tag>
<name>toUpperCase</name>
<tag-class>cc.openhome.ToUpperCaseTag</tag-class>
<body-content>JSP</body-content>
</tag>
</taglib>
接著就如同〈處理標籤屬性與本體〉的示範,可以如下使用這個標籤:
<f:toUpperCase>
<f:forEach var="name" items="${names}">
${name} <br>
</f:forEach>
</f:toUpperCase>