JSP本質其實仍然是Servlet,部署JSP後,服務器會將其自動轉化成Servlet進行運行。用JSP取代Servlet的原因是利用Servlet實現用戶界面比較繁瑣,將大量的靜態HTML代碼穿插於Java代碼中增加了代碼維護工作的難度,並且也不利於前端開發人員和美工人員參與項目開發。
JSP 2 標準爲開發者提供了自定義標籤庫的功能。使用自定義標籤庫的目的跟使用JSP的目的類似,爲了使系統的界面開發和邏輯處理解耦,雖然使用JSP頁面可以將界面開發和負責處業務邏輯的Servlet相分離,但是JSP頁面中依然存在由Java代碼構成的JSP腳本。引入自定義標籤庫技術使得開發者可以用自定義的標籤來取代JSP頁面中的Java腳本。使得前端的開發更加簡潔,也更容易維護。
下文現總結了自定義標籤庫開發步驟。
2、基本用法:
JSP 2 標準下定義自定義標籤庫有以下幾個步驟:
① 開發自定義標籤處理類。② 編寫標籤庫配置文件,格式爲*.tld,一個配置文件配置了一個標籤庫,而一個標籤庫中可以擁有多個標籤。③ 在JSP頁面中使用該標籤。
具體步驟如下:
·開發自定義標籤處理類:
當JSP頁面遇到自定義標籤時,服務器會根據配置文件查找標籤處理類來處理標籤,每一個標籤處理類定義了一個標籤的處理方法。
自定義標籤處理類要求繼承一個父類:javax.servlet.jsp.tagext.SimpleTagSupport。同時還有如下2個要求:
① 如果標籤類中包含屬性,每個屬性必須要有Getter和Setter方法。
② 重寫doTag方法,這個方法定義了標籤的處理過程。
樣例如下:
package tag;
import java.io.IOException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class Tag1 extends SimpleTagSupport{
public void doTag() throws IOException {
getJspContext().getOut().write("<h1>tag test</h1>");
}
}
·編寫標籤庫配置文件 *.tld:
配置文件本質上是一個標準的XML文件。該文件給出了一個標籤庫的配置信息,其中包含了多個標籤的配置信息。這裏給出樣例如下:
<?xml version="1.0" encoding="GBK"?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd"
version="2.0">
<tlib-version>1.0</tlib-version>
<short-name>mytaglib</short-name>
<!-- 定義該標籤庫的URI -->
<uri>abc</uri>
<tag>
<name>tag1</name>
<tag-class>tag.Tag1</tag-class>
<body-content>empty</body-content>
</tag>
</taglib>
① <tlib-version> 該元素指明瞭標籤庫實現的版本,表示標籤庫內部的版本號,這個標籤對程序沒有太大的作用
② <short-name> 該元素指定了標籤庫的默認短命,該名稱也沒有太大的用處。
③ <uri> 這個子元素非常重要,它指明瞭該標籤庫的uri,是標籤庫的唯一標識,JSP頁面中會使用uri來定位標籤庫。這裏我們定義爲:abc。
① <name> 該元素指明瞭標籤的名稱。② <tag-class> 該元素指明瞭標籤的處理類,這裏我們配置了上文中實現的類Tag1。③ <body-content> 該元素指定了標籤體的具體內容,這裏我們設定爲empty,表示該標籤沒有標籤體,只能以空標籤的形式使用,該元素還可以指定以下的值:
tagdependent:指定標籤處理類自己負責處理標籤體。scriptless:指定標籤體可以是靜態的HTML元素、表達式語言(EL),但不允許出現JSP腳本。dynamic-attributes:指定標籤是否支持動態屬性。
·在JSP頁面中使用標籤:
要在JSP頁面中使用自定義標籤,首先需要用編譯指令<%@tablib...%> 導入標籤庫,格式如下:
<%taglib uri="..." prefix="..." %>
其中uri就是tld文件中配置的uri,prefix則表示標籤的前綴,可以隨意設定,對於本例,則寫法如下:
<%taglib uri="abc" prefix="mytag" %>
導入標籤庫後,就可以在頁面中使用自定義標籤了,格式如下:
<prefix:name .../>
其中:prefix就是編譯指令中指定的前綴名,name就是標籤的名稱,也就是tld文件中指定的<tag>元素中的<name>子元素的值,對於本例,寫法如下:
<mytag:tag />
以下爲jsp頁面的樣例:
<%@ page language="java" contentType="text/html; charset=GBK"
pageEncoding="GBK"%>
<%@taglib uri="abc" prefix="mytag"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<mytag:tag1 />
<mytag:tag1 />
</body>
</html>
需要說明的是,在標籤處理過程中,向JSP輸出流中也可以輸出HTML標籤,此時,頁面也會顯示對應的效果,例如將代碼1中的第9行改爲:
3、帶屬性的標籤:
上文示例中定義的標籤沒有屬性,要想定義帶屬性的標籤,要在標籤處理類和標籤庫配置文件中對標籤的屬性進行配置,標籤中的屬性和標籤處理類中的成員變量是一一對應的,並且必須包含對應的Getter和Setter方法。
·靜態屬性標籤:
靜態屬性的標籤包含的屬性個數、屬性名稱都是固定的,由配置文件決定標籤包含哪些屬性。下面定義一個包含2個屬性的標籤tag2,格式如下:
<mytag:tag2 attr1="value1" attr2="value2" />
其中:attr1和attr2爲該標籤的屬性。
首先定義標籤處理類,代碼如下:
package tag;
import java.io.IOException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class Tag2 extends SimpleTagSupport{
String attr1;
String attr2;
public String getAttr1() {
return attr1;
}
public void setAttr1(String attr1) {
this.attr1 = attr1;
}
public String getAttr2() {
return attr2;
}
public void setAttr2(String attr2) {
this.attr2 = attr2;
}
public void doTag() throws IOException {
getJspContext().getOut().write("attr1 = " + attr1 + "<br>"
+ "attr2 = " + attr2);
}
}
<tag>
<name>tag2</name>
<tag-class>tag.Tag2</tag-class>
<body-content>empty</body-content>
<attribute>
<name>attr1</name>
<required>true</required>
<fragment>false</fragment>
</attribute>
<attribute>
<name>attr2</name>
<required>true</required>
<fragment>true</fragment>
</attribute>
</tag>
required:指明該屬性是否爲必須屬性,該子元素的值是true或false。
fragment:設置該屬性是否支持JSP腳本、表達式語言等動態內容,該子元素的值是true或false。
配置完成,然後我們在頁面中使用標籤tag2,在tagTest.jsp的body部分,添加一行新代碼:
<mytag:tag2 attr1="value3" attr2="value4" />
可以看到頁面中顯示我們設定的屬性值。
·動態屬性標籤:
在某些情況,我們可能會用到屬性名稱和屬性數目都不確定的標籤,這樣的標籤稱爲“動態屬性”標籤。要開發具有動態屬性的自定義標籤,需要滿足下面兩個額外的要求:
① 標籤處理類要實現DynamicAttributes接口。
② 配置標籤時通過<dynamic-attributes.../>子元素指定該標籤支持動態屬性。
下面我們定義一個新的標籤tag3,首先在配置文件中添加配置信息如下:
<tag>
<name>tag3</name>
<tag-class>tag.Tag3</tag-class>
<body-content>empty</body-content>
<dynamic-attributes>true</dynamic-attributes>
</tag>
package tag;
import java.io.IOException;
import java.util.HashMap;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.DynamicAttributes;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class Tag3 extends SimpleTagSupport
implements DynamicAttributes {
HashMap<String, Object> attributes = new HashMap<>();
@Override
public void setDynamicAttribute(String uri,
String localName, Object value)
throws JspException {
// TODO Auto-generated method stub
attributes.put(localName, value);
}
public void doTag() throws IOException {
JspWriter writer = getJspContext().getOut();
for(String key : attributes.keySet()) {
writer.write(key + " = " + attributes.get(key) + "<br/>");
}
}
}
<h3>Dynamic attributes tag test:</h3><hr>
運行程序,看到下圖的結果:
·以頁面片段爲屬性的標籤:
JSP2 允許以一段“頁面片段”作爲屬性,這種方式給自定義標籤提供了更大的靈活性。要定義以頁面片段爲屬性的標籤,需要滿足以下要求:
① 標籤處理類中定義類型爲JspFragment的屬性,該屬性代表了“頁面片段”。
② 使用標籤庫時,通過<jsp:attribute.../>動作指定爲標籤庫屬性指定值。
下面定義一個新的標籤tag4,該標籤以頁面片段爲屬性。首先在tld文件爲該標籤配置<tag>元素,如下:
<tag>
<name>tag4</name>
<tag-class>tag.Tag4</tag-class>
<body-content>empty</body-content>
<attribute>
<name>fragment</name>
<required>true</required>
<fragment>true</fragment>
</attribute>
</tag>
package tag;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class Tag4 extends SimpleTagSupport {
JspFragment fragment;
public JspFragment getFragment() {
return fragment;
}
public void setFragment(JspFragment fragment) {
this.fragment = fragment;
}
public void doTag() throws JspException,
IOException {
fragment.invoke(null);
}
}
<mytag:tag4>
<jsp:attritude name="...">
<屬性值>
<jsp:attritude />
<mytag:tag4>
這裏我們用一個新的JSP頁面來測試標籤tag4,代碼如下;
<%@ page language="java" contentType="text/html; charset=GBK"
pageEncoding="GBK"%>
<%@taglib uri="abc" prefix="mytag" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<h3>Page Fragment as an Attribute:</h3><hr>
<mytag:tag4>
<jsp:attribute name="fragment">
<mytag:tag1 />
</jsp:attribute>
</mytag:tag4>
<br>
<mytag:tag4>
<jsp:attribute name="fragment">
<mytag:tag2 attr1="v1" attr2="v2"/>
</jsp:attribute>
</mytag:tag4>
<br><h3>Using Tag Body:</h3><hr>
<mytag:tag5>
<mytag:tag1 />
</mytag:tag5>
<mytag:tag5>
<mytag:tag2 attr1="v1" attr2="v2"/>
</mytag:tag5>
</body>
</html>
筆者認爲,以頁面片段爲屬性的標籤的功能和下文介紹的帶有標籤體的標籤的功能很相似,詳細說明見下文。
本節介紹帶有標籤體的自定義標籤,設計帶有標籤體的自定義標籤很簡單,只要在標籤處理類中的doTag方法中通過getJspBody方法就可以返回封裝了標籤體內容的對象,需要說明的是,該方法的返回值類型就是JspFragment,因此,帶有標籤體的標籤和以頁面片段爲屬性的標籤的功能是類似的。
我們設計一個帶標籤體的標籤tag5,首先標籤處理類代碼如下:
package tag;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class Tag5 extends SimpleTagSupport {
public void doTag() throws JspException,
IOException {
getJspBody().invoke(null);
}
}
<tag>
<name>tag5</name>
<tag-class>tag.Tag5</tag-class>
<body-content>scriptless</body-content>
</tag>
<br><h3>Using Tag Body</h3><hr>
<mytag:tag5><mytag:tag1 /></mytag:tag5><mytag:tag5><mytag:tag2 attr1="v1" attr2="v2"/></mytag:tag5>
僅僅將標籤體的內容輸出到頁面中在實際項目開發中並沒有太大的意義,下面我們設計一個新的標籤tag6,來完成更復雜的功能。
首先是標籤處理類Tag6,代碼如下:
package tag;
import java.io.IOException;
import java.util.Collection;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class Tag6 extends SimpleTagSupport {
private String collectionKey;
private String elementKey;
@SuppressWarnings("rawtypes")
public void doTag() throws JspException,
IOException {
Collection collection = (Collection) getJspContext()
.getAttribute(collectionKey);
for (Object element : collection) {
getJspContext().setAttribute(
elementKey, element);
getJspBody().invoke(null);
}
}
public String getCollection() {
return collectionKey;
}
public void setCollectionKey(
String collectionKey) {
this.collectionKey = collectionKey;
}
public String getElementKey() {
return elementKey;
}
public void setElementKey(String elementKey) {
this.elementKey = elementKey;
}
}
<tag>
<name>tag6</name>
<tag-class>tag.Tag6</tag-class>
<body-content>scriptless</body-content>
<attribute>
<name>collectionKey</name>
<required>true</required>
<fragment>false</fragment>
</attribute>
<attribute>
<name>elementKey</name>
<required>true</required>
<fragment>false</fragment>
</attribute>
</tag>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ page import="java.util.*" %>
<%@ taglib uri="abc" prefix="mytag" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<%
Set set = new HashSet();
set.add(1);
set.add(2);
set.add(3);
pageContext.setAttribute("collection", set);
%>
<h3>Iterator Tag:</h3><hr>
<mytag:tag6 collectionKey="collection" elementKey="element">
${pageScope.element}<br>
</mytag:tag6>
</body>
</html>