如果想要在本體執行過後,取得執行的結果並作適當處理該如何進行?例如實作一個〈處理標籤屬性與本體〉的 <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>

