在 JSP 開發中,常見模式是:自定義標籤處理複雜業務邏輯(如權限計算、資料轉換),將結果放入請求屬性,再由 JSTL 標籤(<c:out>、<c:forEach> 等)配合 EL 表達式完成顯示。關鍵在於:如何讓自定義標籤產出的資料被 EL 正確識別?
<%@ taglib uri="/WEB-INF/tlds/diego.tld" prefix="diego" %>
<%@ taglib uri="/WEB-INF/tlds/c.tld" prefix="c" %>
<%
Man man = new Man(); man.setName("diego");
request.setAttribute("man", man);
%>
<!-- 自定義標籤:取得 man 物件的 name 屬性,存入 myname -->
<diego:set value="${man}" property="name" var="myname" />
<!-- JSTL 輸出結果 -->
value is : <c:out value="${myname}" />
輸出結果:value is : diego
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;
// setter 方法對應 TLD 屬性定義
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; }
public int doEndTag() throws JspException {
try {
// 1. 利用 BeanUtils 取得巢狀屬性值
Object propertyValue = PropertyUtils.getProperty(value, property);
// 2. 存入 pageContext(請求範圍),供後續 EL 存取
pageContext.setAttribute(var, propertyValue);
} catch (Exception e) {
throw new JspException(e);
}
return EVAL_PAGE;
}
}
<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>
ExpressionEvaluatorManager.evaluate() 是 JSTL 內部工具類,負責將字串形式的 EL 表達式(如 "${man}")在當前 pageContext 中解析為實際物件。這是自定義標籤支援 EL 的核心橋樑。
pageContext.setAttribute(var, value) 預設存入 PageScope,但 EL 表達式 ${myname} 會依序搜尋 Page → Request → Session → Application 範圍,因此能被 <c:out> 正確找到。
支援巢狀屬性路徑(如 "address.city.name")、Map 鍵值、List 索引等,極大擴展了標籤適用場景。
List<User> 轉為 List<String>(只取名字),供 <c:forEach> 直接迭代。boolean 供 <c:if> 使用。rtexprvalue="true" 並正確求值。pageContext.setAttribute(PageScope),避免汙染 Request/Session。JspException,確保 JSP 編譯期/執行期錯誤可被容器統一處理。遵循此模式,團隊可將「業務邏輯」與「視圖渲染」徹底解耦,Java 開發者專注標籤開發,頁面設計師專注 JSTL+EL 模板編寫,極大提升協作效率與代碼復用率。