之前我們介紹的Tag都是以實作interface Tag來達到效果。而Interface Tag有多個method,並且在不同的時候返回不同的值來控制流程。
但是當我們在實際使用的時候,通常來說我們不會需要控制這些流程,我們只會override doEndTag()而已,就為了這個要實作並且返回奇怪的數字看起來是在很麻煩。
因此在jsp 2.x之後,除了支援舊的格式以外,有多增加了一個interface叫做SimpleTag讓我們開發自定義tag的時候更加容易。
這一篇我們來看一下和interface Tag有什麼不一樣。
(和我部落格同時發佈:http://www.dotblogs.com.tw/alantsai/archive/2013/10/20/tag-simple_tag_support.aspx)
SimpleTag的架構
SimpleTag很簡單,只需要實作一個void doTag()的方法。和Tag一樣,有一個模板的class叫做SimpleTagSupport我們可以直接繼承這個class來開發自定義Tag。
SimpleTag和Tag一樣都支援Attribute和取得tag body內容。
SimpleTag的tld定義基本上和Tag一樣。
SimpleTag取得Attribute
和Tag一樣,都是設定tld然後有一個對應一樣名稱的variable,例如:
public class TestSimpleTag extends SimpleTagSupport {
int a;
int b;
public void setA(int a) {
this.a = a;
}
public void setB(int b) {
this.b = b;
}
@Override
public void doTag() throws JspException, IOException {
this.getJspContext().getOut().println(a + " * " + b + " = " + a*b);
}
}
然後tld一樣:
<tag>
<name>multiple</name>
<tag-class>tag.TestSimpleTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>a</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>b</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
這樣就完成了使用Attribute的範例。
SimpleTag取得body 內容
相較於取得Attribute,要取得body內容就不太一樣了。
SimpleTag裡面有一個JspFragment的object我們可以呼叫,而那個object就代表著body的內容。
簡單的範例,把body文字全部轉成小寫:
@Override
public void doTag() throws JspException, IOException {
JspFragment body = this.getJspBody();
//write the content of JspFragment into writer
StringWriter writer = new StringWriter();
body.invoke(writer);
String content = writer.toString();
JspWriter out = this.getJspContext().getOut();
out.println(content.toLowerCase());
}
這一次要記得在tld裡面content的部份要修改:
<tag>
<name>multiple</name>
<tag-class>tag.TestSimpleTag</tag-class>
<body-content>tagdependent</body-content>
</tag>
呼叫我就不顯示了。
可以看得出來,我們取得JspFragment有點像是一個body內容的buffer,需要把它寫出(invoke())來才能用。因此我們能夠對body內容做修改。
多個body標籤
在SimpleTag裡面我們可以擁有多個標籤body,並且我們也可以像在Tag裡面一樣,多次呼叫body去執行,做法如下:
public class TestSimpleTag extends SimpleTagSupport {
private JspFragment body1;
private JspFragment body2;
public void setBody1(JspFragment body1) {
this.body1 = body1;
}
public void setBody2(JspFragment body2) {
this.body2 = body2;
}
@Override
public void doTag() throws JspException, IOException {
//write the content of JspFragment into writer
StringWriter writer = new StringWriter();
for (int i = 0; i < 2; i++) {
body1.invoke(writer);
}
StringWriter writer2 = new StringWriter();
for (int i = 0; i < 3; i++) {
body2.invoke(writer2);
}
String content = writer.toString();
String content2 = writer2.toString();
JspWriter out = this.getJspContext().getOut();
out.println("content 1: " + content);
out.println("content 2: " + content2);
}
}
和定義attribute很像,我們先定義兩個body JspFragment。可以看到在執行的時候我們多次執行了個自裡面的body內容,這個和TagBodySupport裡面在doAfterBody() 重複return一樣道理。
接下來在tld裡面我們要註明這個是fragment:
<tag>
<name>multiple</name>
<tag-class>tag.TestSimpleTag</tag-class>
<body-content>tagdependent</body-content>
<attribute>
<name>body1</name>
<required>false</required>
<fragment>true</fragment>
</attribute>
<attribute>
<name>body2</name>
<required>false</required>
<fragment>true</fragment>
</attribute>
</tag>
最後在呼叫的時候,利用jsp action tag的attribute來使用:
<hello2:multiple>
<jsp:attribute name='body1'>body1,</jsp:attribute>
<jsp:attribute name='body2'>body2,</jsp:attribute>
</hello2:multiple>
就可以了。
結語
自定義Tag其實因為版本不同因此有兩種做法。使用Jsp 1.x(實作Tag interface)的方法讓我們對於執行流程有更多的控制,不過大部份情況我們都不需要控制到這麼細,因此在jsp 2.x才有了SimpleTag的interface出現。
希望透過這篇能夠瞭解如何使用SimpleTag。在下一篇我們會提到如何自定義tag function,就像jstl裡面的fmt那樣。