自定義標籤處理完業務邏輯後,希望將結果暴露給頁面上的 JSTL 標籤(如 <c:out>、<c:forEach>)直接使用,關鍵在於將數據放入頁面上下文(PageContext/Request)並支持 EL 表達式解析。
doEndTag() 中通過 pageContext.setAttribute(var, value) 發布變量。ExpressionEvaluatorManager.evaluate(...) 解析 EL 表達式(${name})。<my:set> 標籤<tag>
<name>set</name>
<tag-class>diegoyun.SetVarTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>value</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue> <!-- 支持運行時表達式/EL -->
</attribute>
<attribute>
<name>property</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
<attribute>
<name>var</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
</tag>
package diegoyun;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.taglibs.standard.lang.support.ExpressionEvaluatorManager;
public class SetVarTag extends TagSupport {
private Object value;
private String property;
private String var;
public void setValue(Object value) throws JspException {
// 關鍵:解析 EL/運行時表達式
this.value = ExpressionEvaluatorManager.evaluate(
"value", value.toString(), Object.class, this, pageContext);
}
public void setProperty(String property) { this.property = property; }
public void setVar(String var) { this.var = var; }
@Override
public int doEndTag() throws JspException {
Object propertyValue = null;
try {
// 從 value 對象提取屬性(支持巢狀/索引)
propertyValue = PropertyUtils.getProperty(value, property);
} catch (Exception e) {
throw new JspException(e);
}
// 發布到頁面上下文,JSTL 即可通過 ${var} 訪問
pageContext.setAttribute(var, propertyValue);
return EVAL_PAGE;
}
}
<%@ taglib prefix="my" uri="https://example.com/tags" %>
<%@ taglib prefix="c" uri="https://java.sun.com/jsp/jstl/core" %>
<% request.setAttribute("user", new User("Alice", "admin")); %>
<!-- 從 user 對象提取 role 屬性,存為 pageScope 變量 currentRole -->
<my:set value="${user}" property="role" var="currentRole"/>
<!-- JSTL 直接使用 -->
<c:out value="${currentRole}"/>
<c:if test="${currentRole == 'admin'}">管理員面板</c:if>
| 技術點 | 說明 |
|---|---|
rtexprvalue=true |
允許屬性值為 EL 表達式(${...})或腳本表達式(<%=...%>),容器會在調用 setter 前自動求值。 |
ExpressionEvaluatorManager.evaluate |
手動解析表達式字符串,參數含義: 1. 屬性名(用於錯誤提示) 2. 表達式字符串 3. 期望返回類型 4. Tag 實例(提供 pageContext)5. PageContext(作用域查找) |
PropertyUtils.getProperty |
Commons BeanUtils 提供,支持 user.address.city、list[0].name 等巢狀/索引路徑。 |
pageContext.setAttribute(var, value) |
默認存入 PageContext.PAGE_SCOPE,也可指定 REQUEST/SESSION/APPLICATION_SCOPE。 |
若自定義標籤需要對體內容進行多次渲染(如 <c:forEach>),需實現 BodyTag 接口,利用 doInitBody、doAfterBody 協同 BodyContent.getString() 獲取體內容,配合 EVAL_BODY_BUFFERED、EVAL_BODY_AGAIN 控制迭代流程。
自定義標籤與 JSTL/EL 協作的核心契約是:標籤負責「數據準備+作用域發布」,JSTL 負責「表達式解析+渲染」。遵循 rtexprvalue=true、ExpressionEvaluatorManager、PropertyUtils、pageContext.setAttribute 四步曲,即可無縫融入 JSTL 生態。