標籤處理器生命週期與調用順序
<c:if test="...">
<c:out value="..."/>
</c:if>
| 方法 | 觸發時機 | 典型返回值 | 說明 |
doStartTag() |
容器解析到開始標籤 <c:if> |
EVAL_BODY_INCLUDE / SKIP_BODY |
決定是否處理標籤體。返回 SKIP_BODY 則跳過體內容(含嵌套標籤)。 |
doInitBody() |
開始標籤結束 > 後、體內容前(僅 BodyTag) |
無返回值 |
初始化 BodyContent,可預置輸出緩衝區。 |
doAfterBody() |
體內容處理完畢、遇到 </c:out> 或體結束 |
EVAL_BODY_AGAIN / SKIP_BODY |
迭代標籤返回 EVAL_BODY_AGAIN 觸發再次評估體;否則返回 SKIP_BODY 結束循環。 |
doEndTag() |
容器解析到結束標籤 </c:if> |
EVAL_PAGE / SKIP_PAGE |
返回 SKIP_PAGE 直接結束整頁渲染(如 <c:redirect>)。 |
核心返回值常量速查
| 常量 | 數值 | 適用方法 | 語義 |
EVAL_BODY_INCLUDE | 1 | doStartTag | 評估標籤體,結果輸出到當前 JspWriter(不緩衝)。 |
EVAL_BODY_BUFFERED | 2 | doStartTag (BodyTag) | 評估標籤體,結果捕獲到 BodyContent 緩衝區,供 doInitBody/doAfterBody 處理。 |
SKIP_BODY | 0 | doStartTag, doAfterBody | 跳過標籤體處理。 |
EVAL_BODY_AGAIN | 2 | doAfterBody | 再次評估標籤體(迭代標籤核心)。 |
EVAL_PAGE | 6 | doEndTag, doStartTag, doAfterBody | 繼續處理頁面其餘內容。 |
SKIP_PAGE | 5 | doEndTag | 立即停止頁面處理,拋出 SkipPageException 內部流轉。 |
高頻實用工具方法
| 需求 | 代碼片段 | 依賴包 |
| 支持 EL 表達式求值 |
ExpressionEvaluatorManager.evaluate("attr", expr, ExpectedType.class, this, pageContext) |
standard.jar (JSTL) |
| 從 Bean 提取巢狀屬性 |
PropertyUtils.getProperty(bean, "address.city.zip") |
commons-beanutils.jar |
| 向頁面作用域發布變量 |
pageContext.setAttribute("varName", value) |
JSP API |
| 向輸出流寫入內容 |
pageContext.getOut().print(html) |
JSP API |
| 查找祖先標籤(跨層級協作) |
TagSupport.findAncestorWithClass(this, ParentTagClass.class) |
JSP API |
| 獲取父標籤 |
getParent() |
JSP API |
標籤類型選擇決策樹
- 無標籤體、無迭代 → 繼承
TagSupport,實現 doStartTag/doEndTag。
- 需處理標籤體內容(讀取/修改/緩衝) → 繼承
BodyTagSupport,實現 doInitBody/doAfterBody。
- 需迭代標籤體多次 →
doAfterBody 返回 EVAL_BODY_AGAIN,配合計數器控制次數。
- 需提前終止頁面 →
doEndTag 返回 SKIP_PAGE。
避坑清單
- 釋放資源:
doFinally() (JSP 2.0+) 或 release() 中重置所有字段,防止標籤處理器池化復用導致狀態洩漏。
- BodyContent 獲取:
getBodyContent().getString() 只能在 doAfterBody/doEndTag 中調用,在此之前為 null。
- EL 求值異常:捕獲
JspException 包裝拋出,保留原始堆棧便於調試。
- 線程安全:標籤處理器實例可被多線程復用,絕不可 在字段保存請求級狀態,所有數據必須通過
pageContext 傳遞。
總結
掌握 doStartTag → doInitBody → doAfterBody → doEndTag 四階段生命週期、六大返回值常量語義、五大高頻工具方法,即可應對 95% 自定義標籤開發場景。關鍵在於根據業務需求選擇正確的標籤基類,並嚴守無實例字段存儲請求狀態的線程安全鐵律。