From Gossip@caterpillar

JSP/Servlet: Scripting Variable

 

 


Scripting Variable是用於自訂標籤與JSP頁面之間溝通的變數,您可能會需要取出標籤運算之後的值,以便在接下來的JSP頁面中進行運算,例如取出某個標籤運算後的值,設定為另一個標籤運算的屬性值。

有幾種方式可以用於設定Scripting Variable,主要的概念都是在JSP頁面轉譯為Servlet時,透過一個中介者讓Container知道那些變數該轉譯為Scripting Variable,以便JSP頁面與自訂標籤可以共用這個變數,這個類別可以是一個類別、也可以是一個web.xml中的描述。

設定Scripting Variable的一個方法,就是將之設定給pageContext,然後再告訴Container,設定pageContext的方法如下:
pageContext.setAttribute("varname", vardata);
 

來看一個例子,首先我們撰寫下面這個類別:

  • DecodeTag.java
package onlyfun.caterpillar;

import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;

public class DecodeTag extends TagSupport {
private String code;

public void setCode(String code) {
this.code = code;
}

public int doStartTag() throws JspException {
code = code + "-decoded";
pageContext.setAttribute("decoded", code);

return SKIP_BODY;
}
}

這個類別模擬解碼的過程,decoded是用來作為Scripting Variable的變數名稱,接下來必須告知容器這個資訊,作法之一,就是透過TagExtraInfo類別與VariableInfo類別,直接來看如何撰寫:
  • TagExtraDemo.java
package onlyfun.caterpillar;

import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;

public class TagExtraDemo extends TagExtraInfo {
public VariableInfo[] getVariableInfo(TagData data) {
VariableInfo info = new VariableInfo("decoded",
"String", true, VariableInfo.AT_END);

return new VariableInfo[] {info};
}
}

getVariableInfo()方法傳回VariableInfo的陣列值,陣列值的內容就是Scripting Variable的相關資訊,在VariableInfo中的建構中傳入四個參數:Scripting Variable名稱、Scripting Variable型態、之前有無宣告過、作用範圍。

第三個參數若設定為true,表示之前有宣告過,直接使用宣告過的變數,如果為false,則會生成新的實例;第四個參數為Scripting Variable在JSP頁面中可以作用的範圍,分為三種:
  • VariableInfo.AT_BEGIN:作用範圍從標籤開始至JSP頁面結束。
  • VariableInfo.AT_END:作用範圍從標籤結束至JSP頁面結束。
  • VariableInfo.AT_NESTED:作用範圍從標籤開始至標籤結束。

接下來在tld檔中告訴容器有關於自訂標籤及TagExtraInfo類別的資訊:

  • decode.tld
... 
<tag>
<description>Decode</description>
<name>decode</name>
<tag-class>onlyfun.caterpillar.DecodeTag</tag-class>
<tei-class>onlyfun.caterpillar.TagExtraDemo</tei-class>
<body-content>empty</body-content>
<attribute>
<name>code</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
...

其中<tei-class>標籤即用來告訴容器有關於TagExtraInfo類別的資訊,容器將JSP頁面轉譯為Servlet時就會用到這個資訊,可以這麼測試:
  • test.jsp
<%@taglib prefix="caterpillar"  
uri="http://caterpillar.onlyfun.net/"%>
<html>
<body>

解碼前:${ param.code } <br>

<caterpillar:decode code="${ param.code }"/>

解碼後:${ decoded }

</body>
</html>

上面這個方法的好處是使用一個類別集中管理標籤的Scripting Variable,缺點則是若要修改變數,則必須修改TagExtraInfo類別後重新編譯等;另一個方法則只要在tld檔案中直接設定即可,不用透過 TagExtraInfo類別,設定的例子如下:
  • decode.tld
... 
<tag>
<description>Decode</description>
<name>decode</name>
<tag-class>onlyfun.caterpillar.DecodeTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>code</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name-given>decoded</name-given>
<variable-class>String</variable-class>
<declare>true</declare>
<scope>AT_END</scope>
</attribute>
</tag>
...

這次不需要透過<tei-class>的指定了,所使用的是<name-given>、< variable-class>、<declare>與<scope>四個標籤,其意義與VariableInfo建構時 的四個參數相同。

使用固定的變數名稱,則使用自訂標籤的人員必須事先知道Scripting Variable的名稱,才可以在JSP頁面中呼叫使用它,也可以讓使用自訂標籤的人員自行決定名稱,方法是使用<name-from- attribute>,例如:

  • decode.tld
... 
<tag>
<description>Decode</description>
<name>decode</name>
<tag-class>onlyfun.caterpillar.DecodeTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>code</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>varname</name>
<required>true</required>
</attribute>
<attribute>
<name-from-attribute>varname</name-from-attribute>
<variable-class>String</variable-class>
<declare>true</declare>
<scope>AT_END</scope>
</attribute>
</tag>
...

然後自訂標籤處理類別必須作一些修改,使其能夠接收varname屬性:
  • DecodeTag.java
package onlyfun.caterpillar;

import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;

public class DecodeTag extends TagSupport {
private String varname;
private String code;

public void setVarname(String varname) {
this.varname = varname;
}

public void setCode(String code) {
this.code = code;
}

public int doStartTag() throws JspException? {
code = code + "-decoded";
pageContext.setAttribute(varname, code);

return SKIP_BODY;
}
}

接下來就可以在JSP頁面中這麼使用:
  • test.jsp
<%@taglib prefix="caterpillar" 
uri="http://caterpillar.onlyfun.net/"%>
<html>
<body>

解碼前:${ param.code } <br>

<caterpillar:decode varname="keyword" code="${ param.code }"/>

解碼後:${ keyword }

</body>
</html>

如果您透過繼承SimpleTagSupport類別來實作自訂標籤,則在設定Scripting Variable時,可以簡單的使用JspContext的setAttribute()方法來設定,而不需要額外的設定tld檔案,例如:
  • SimpleDecodeTag.java
package onlyfun.caterpillar;

import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;

public class SimpleDecodeTag extends SimpleTagSupport {
private String code;

public void setCode(String code) {
this.code = code;
}

public void doTag() throws JspException {
code = code + "-decoded";
getJspContext().setAttribute("decoded", _code);
}
}

您可以使用下面這個JSP網頁進行測試:
  • test.jsp
<%@taglib prefix="caterpillar" 
uri="http://caterpillar.onlyfun.net/"%>
<html>
<body>

解碼前:${ param.code } <br>

<caterpillar:decode code="${ param.code }"/>

解碼後:${ decoded }

</body>
</html>