Taglib原理和實現
Tag究竟是什么?如何實現一個Tag?
一個tag就是一個普通的java類,它惟一特別之處是它必須繼承TagSupport或者BodyTagSupport類。這兩個類提供了一些方法,負責jsp頁面和你編寫的類之間的交互,例如輸入,輸出。而這兩個類是由jsp容器提供的,無須開發人員自己實現。換句話說,你只需把實現了業務邏輯的類繼承TagSupport或者BodyTagSupport,再做一些特別的工作,你的類就是一個Tag。并且它自己負責和jsp頁面的交互,不用你多操心。
“特別的工作”通常有以下幾個步驟:
1)提供屬性的set方法,此后這個屬性就可以在jsp頁面設置。以jstl標簽為例 c:out value=""/,這個value就是jsp數據到tag之間的入口。所以tag里面必須有一個setValue方法,具體的屬性可以不叫value。例如setValue(String data){this.data = data;}
這個“value”的名稱是在tld里定義的。取什么名字都可以,只需tag里提供相應的set方法即可。
2)處理 doStartTag 或 doEndTag 。這兩個方法是 TagSupport提供的。 還是以c:out value=""/為例,當jsp解析這個標簽的時候,在“<”處觸發 doStartTag 事件,在“>”時觸發 doEndTag 事件。通常在 doStartTag 里進行邏輯操作,在 doEndTag 里控制輸出。
3)編寫tld文件。
4)在jsp頁面導入tld。這樣,你的jsp頁面就可以使用自己的tag了。
通常你會發現自己絕大多數活動都集中在 doStartTag 或 doEndTag 方法里。確實如此,熟悉一些接口和類之后,寫taglib很容易。正如《jsp設計》的作者所言:里面的邏輯稍微有點復雜,但畢竟沒有火箭上天那么難。
一個簡單的例子:OutputTag
package diegoyun;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.TagSupport;
/**
* @author chenys
*/
public class OutputTag extends TagSupport
{
private String name=null;
public void setName(String name)
{
this.name = name;
}
public int doStartTag() throws JspException{
try
{
JspWriter out = pageContext.getOut();
out.print("Hello! " + name);
}
catch (Exception e)
{
throw new JspException(e);
}
return EVAL_PAGE;
}
}
|
簡要說明:
1 如何輸出到jsp頁面:調用JspWriter JspWriter out = pageContext.getOut();out.print......記住這個方法就可以了。
2 輸出后如何作處理,函數會返回幾個值之一。EVAL_PAGE 表示tag已處理完畢,返回jsp頁面。還有幾個值,例如 EVAL_BODY_AGAIN 和EVAL_BODY_INCLUDE等,后面我們會作討論。
編寫tld
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE taglib
PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
"http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
<tlib-version>1.0</tlib-version>
<jsp-version>1.2</jsp-version>
<short-name>diego</short-name>
<!--OutputTag-->
<tag>
<name>out</name>
<tag-class>diegoyun.OutputTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>name</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
</tag>
</taglib>
|
在WEB-INF下新建tlds文件夾,把這個文件取名為diego.tld,放到tlds文件夾下。路徑應該這樣:WEB-INF\tlds\diego.tld
關于tld的簡單說明:
short-name:taglib的名稱,也稱為前綴。比如“c:out value=""/” 里的“c”
name:tag的名字。例如“c:out value=""/” 里的"out”,我們的類也取名為out,由于有前綴作區分,不會混淆
tag-class:具體的tag類。帶包名
body-content:指tag之間的內容。例如c:out value="" ...... /c 起始和關閉標簽之間就是body-content。由于沒有處理body-content,所以上面設為empty
“attribute”里的name:屬性名字。例如c:out value=""/里的value。名字可任意取,只要類里提供相應的set方法即可。
required:是否必填屬性。
rtexprvalue:是否支持運行時表達式取值。這是tag的強大功能。以后我們會討論。暫時設為false
編寫jsp頁面
<%@ page language="java"%>
<%@ taglib uri="/WEB-INF/tlds/diego.tld" prefix="diego"%>
<html>
<body>
Test Tag:
<diego:out name="diegoyun"/>
</body>
</html>
|
我的編程環境是eclipse+tomcat.啟動服務器,如果一切按照上面步驟的話,就能看到 Test Tag: Hello! diegoyun 字樣。最簡單的tag就這么出來了。并不難,是不是?(T111)
================================================================
Taglib 原理和實現:第二章 讓Tag支持El表達式
-
1.先看這么一個例子
<%@ page contentType="text/html; charset=gb2312" language="java"%>
<%@ taglib uri="/WEB-INF/tlds/c.tld" prefix="c"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<body>
<%
String tut = "tutorial";
request.setAttribute("tut",tut);
%>
The String in request is :
<c:out value="${tut}"/>
</body>
</html>
2.如何支持el表達式
在路徑org.apache.taglibs.standard.lang.support下,有個叫ExpressionEvaluatorManager.evaluate 的方法,當el表達式作為入參時,調用這個方法,在tag內即可自動把el表達式轉化。例如,你想tag的value字段支持el表達式,那么只需在 set方法里如下調用:
public void setValue(Object value)throws JspException
{
this.value = ExpressionEvaluatorManager.evaluate(
"value", value.toString(), Object.class, this, pageContext);
}
ExpressionEvaluatorManager.evaluate有四個參數。第一個表示tag的名字,在取el表達式出錯時使用。一般和屬性名 字相同。第二個要求字符串,通常簡單調用輸入對象的toString方法。第三個是類,通常用Object.class。第四個用this即可,第五個是 pageContext變量。
通常不用對這個方法思考太多。只需改改屬性名字,其他照搬即可。
注意:當你的tag屬性支持el表達式時,你必須把它聲明為Object對象。如上述的value,應該聲明為:
private Object value = null;
3.實例:讓OutputTag支持El表達式
package diegoyun;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.TagSupport;
import org.apache.taglibs.standard.lang.support.ExpressionEvaluatorManager;
-
public class NewOutputTag extends TagSupport
{
private Object name = null;
public void setName(Object name) throws JspException
{
this.name = ExpressionEvaluatorManager.evaluate(
"name", name.toString(), Object.class, this, pageContext);
}
public int doStartTag() throws JspException{
try
{
JspWriter out = pageContext.getOut();
out.print("Hello! " + name);
}
catch (Exception e)
{
throw new JspException(e);
}
return EVAL_PAGE;
}
}
在diego.tld里添加聲明
<!--NewOutputTag-->
<tag>
<name>newout</name>
<tag-class>diegoyun.NewOutputTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>name</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
編寫jsp測試
<%@ page language="java" %>
<%@ taglib uri="/WEB-INF/tlds/diego.tld" prefix="diego"%>
<html>
<body bgcolor="#FFFFFF">
<%
String s = "diego";
request.setAttribute("name",s);
%>
Test El supported tag:
<br>
<diego:newout name="${name}"/>
</body>
</html>
可以看到頁面輸出為:
Test El supported tag:
Hello! diego
=================================================
Taglib 原理和實現:第三章 tag之間的嵌套和屬性讀取
1。問題:在request里有一個 Man 對象,它有兩個屬性:name和age。現在,我們想用一個嵌套的tag,父tag取得對象,子tag取得name屬性并顯示在頁面上。例如,它的形式如下:
<diego:with object="${Man}">
<diego:output property="name"/>
</diego:with>
object 支持el表達式,表示取得 Man 對象。output的property表示從該對象取得名為name的屬性。
2。如何支持tag之間的嵌套
在子tag里調用getParent 方法,可以得到父tag對象。用 findAncestorWithClass 方法,則可以通過遞歸找到想要找的tag。例如
<diego:with object="${people}"> <!--表示取得一個對象-->
< diego:withCollection property="men"> <!--表示取得對象里的一個屬性,這個屬性是個 Collection,Collection里添加了許多man,每個man有名字和年齡-->
<diego:output property="name"/> <!--取得name屬性并顯示-->
</diego:withCollection>
</diego:with>
對于最內層的outputTag來說,調用getParent,可以得到 withCollectionTag,
通過如findAncestorWithClass(this,WithTag.class)的方式,可以得到withTag
得到Tag之后,就可以取得Tag的屬性,進行業務邏輯處理,然后輸出到jsp
3。如何支持類屬性查找功能
顯然,在上面的outputTag中,我們要根據屬性的名字,查找類中有沒有這個屬性。然后取出屬性的值并顯示。通常,這可以編寫自己的反射函數來完成。 更簡單的辦法,是通過 BeanUtil 的PropertyUtils方法來完成功能。BeanUtil 是apache上的一個開源項目。
示例如下:
import org.apache.commons.beanutils.PropertyUtils;
。。。。。。
property = PropertyUtils.getProperty(currentClass, propertyName);
propertyName是待查找屬性的名字,例如上面的"name",currentClass是待查找的類,例如上面的People
記得把 commons-beanutils.jar添加到WEB-INF\lib目錄下
4。現在讓我們實現開篇提出的問題,編寫WithTag如下:
package diegoyun;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;
import org.apache.taglibs.standard.lang.support.ExpressionEvaluatorManager;
/**
* @author chenys
*/
public class WithTag extends BodyTagSupport
{
private Object value = null;
private Object output = null;
public void setOutput(Object output)
{
this.output = output;
}
public Object getValue()
{
return value;
}
public void setValue(Object value)throws JspException
{
this.value = ExpressionEvaluatorManager.evaluate(
"value", value.toString(), Object.class, this, pageContext);
}
public int doStartTag()
{
return EVAL_BODY_INCLUDE;
}
public int doEndTag()throws JspException
{
try
{
pageContext.getOut().print(output);
}
catch (IOException e)
{
throw new JspException(e);
}
return EVAL_PAGE;
}
}
編寫 NestedOutputTag 如下:
package diegoyun;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;
import org.apache.commons.beanutils.PropertyUtils;
/**
* @author chenys
*/
public class NestedOutputTag extends BodyTagSupport
{
private String property = null;
public void setProperty(String property)
{
this.property = property;
}
public int doEndTag()throws JspException
{
WithTag parent =(WithTag)getParent();
if(parent==null)
throw new JspException("Can not find parent Tag ");
try
{
Object propertyValue = PropertyUtils.getProperty(parent.getValue(), property);
parent.setOutput(propertyValue);
}
catch (Exception e)
{
throw new JspException(e);
}
return EVAL_PAGE;
}
}
在包diegoyun下添加一個包vo,在vo下寫一個Man類:
package diegoyun.vo;
/**
* @author chenys
*/
public class Man
{
private String name = null;
private int age = 0;
public int getAge()
{
return age;
}
public void setAge(int age)
{
this.age = age;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
}
寫tld
<!--WithTag-->
<tag>
<name>with</name>
<tag-class>diegoyun.WithTag</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>value</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<!--OutputTag3-->
<tag>
<name>nestedout</name>
<tag-class>diegoyun.NestedOutputTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>property</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
</tag>
寫jsp頁面
<%@ page language="java" %>
<%@ page import="diegoyun.vo.*"%>
<%@ taglib uri="/WEB-INF/tlds/diego.tld" prefix="diego"%>
<html>
<body bgcolor="#FFFFFF">
<%
Man man = new Man();
man.setName("diego");
request.setAttribute("man",man);
%>
Test nested tag:
<br>
<diego:with value="${man}">
<diego:nestedout property="name"/>
</diego:with>
</body>
</html>
運行頁面,則可以看到:
Test nested tag:
diego
5。結束語:
上述例子簡單描繪了嵌套的Tag之間如何交互。通常子Tag負責取得數據,然后設置父Tag的屬性,最后在父Tag里顯示到jsp頁面。如上面的例子,父 Tag 的 output 表示待打印的對象,通過 nestedoutTag 取得name的值,設置output,然后打印出來。
通過支持El表達式和動態屬性聯結,Tag可以實現強大的處理功能。將邏輯都集中到Tag里,極大的簡化頁面的編寫。
=================================================================
Taglib 原理和實現:第四章 循環的Tag
1。問題:在request里的 People 對象,有個屬性叫 men ,men 是一個Collection ,有許多個man 。現在,把 collection里的man的名字都顯示出來
顯然,這是一個嵌套Tag的問題。有三個Tag互相作用:最外層的Tag找到People對象,中間的Tag取得Collection,子Tag負責打印。
例如:
<diego:withObject value="${people}">
<diego:withCollection property="men">
<diego:elementout property="name"/>
</diego:withCollection>
</diego:withObject>
思路如下:
1.編寫WithObjectTag,負責從El表達式中取得對象
2.編寫WithCollectionTag,負責從對象中取得 Collection ,遍歷 Collection ,每遍歷一次 Collection ,執行一次body
3.編寫ElementoutTag ,把 Collection 中每個men對象的 name 打印出來
2. 完整程序如下:
在上例的diegoyun.vo包內,編寫 People 類
package diegoyun.vo;
import java.util.Collection;
public class People
{
private Collection men = null;
public Collection getMen()
{
return men;
}
public void setMen(Collection men)
{
this.men = men;
}
}
編寫 withObject ,這是從request里取得People對象的最外層Tag
package diegoyun;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;
import org.apache.taglibs.standard.lang.support.ExpressionEvaluatorManager;
public class WithObjectTag extends BodyTagSupport
{
private Object value = null;
public Object getValue()
{
return value;
}
public void setValue(Object value)throws JspException
{
this.value = ExpressionEvaluatorManager.evaluate(
"value", value.toString(), Object.class, this, pageContext);
}
public int doStartTag()
{
return EVAL_BODY_INCLUDE;
}
public int doEndTag()throws JspException
{
return EVAL_PAGE;
}
}
編寫WithCollectionTag,該Tag負責取得Collection,并遍歷執行子Tag
package diegoyun;
import java.util.Collection;
import java.util.Iterator;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;
import org.apache.commons.beanutils.PropertyUtils;
public class WithCollectionTag extends BodyTagSupport {
private Object element = null;
private Collection list = null;
private Iterator iterator = null;
public Object getElement() {
return element;
}
public void setProperty(String property) throws JspException {
//取得父Tag對象,并且得到Collection
WithObjectTag parent = (WithObjectTag) getParent();
if (parent == null)
throw new JspException("parent tag is null");
try {
Object propertyValue = PropertyUtils.getProperty(parent.getValue(),
property);
this.list = (Collection) propertyValue;
if (list == null)
throw new JspException("Collection is null");
} catch (Exception e) {
throw new JspException(e);
}
}
public int doStartTag() throws JspException {
//設置第一個元素,然后執行子Tag
iterator = list.iterator();
if (iterator.hasNext())
element = iterator.next();
return EVAL_BODY_INCLUDE;
}
public int doAfterBody() {
if (iterator.hasNext()) {
//如果還存在子元素,設置子元素,并且再次執行子Tag
//循環由此而來
//否則不再執行子Tag
element = iterator.next();
return EVAL_BODY_AGAIN;
}
else
return EVAL_PAGE;
}
}
編寫 ElementOutputTag
package diegoyun;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;
import org.apache.commons.beanutils.PropertyUtils;
public class ElementOutputTag extends TagSupport
{
private Object propertyValue = null;
public void setProperty(String property)throws JspException
{
WithCollectionTag parent = (WithCollectionTag)getParent();
if(parent == null)
throw new JspException("parent tag is null");
try
{
//判斷上層tag中是否存在該屬性名稱,如果存在,取得屬性值,否則報錯
propertyValue = PropertyUtils.getProperty(parent.getElement(), property);
}
catch (Exception e)
{
throw new JspException(e);
}
}
public int doEndTag()throws JspException
{
try
{
//簡單的把值打印到jsp頁面
pageContext.getOut().print(propertyValue);
}
catch (IOException e)
{
throw new JspException(e);
}
return EVAL_PAGE;
}
}
編寫tld
<!--WithObjectTag-->
<tag>
<name>withObject</name>
<tag-class>diegoyun.WithObjectTag</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>value</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<!--WithCollectionTag-->
<tag>
<name>withCollection</name>
<tag-class>diegoyun.WithCollectionTag</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>property</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<!--ElementOutputTag-->
<tag>
<name>elementout</name>
<tag-class>diegoyun.ElementOutputTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>property</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
編寫jsp
<%@ page language="java" %>
<%@ page import="diegoyun.vo.*"%>
<%@ page import="java.util.*"%>
<%@ taglib uri="/WEB-INF/tlds/diego.tld" prefix="diego"%>
<html>
<body bgcolor="#FFFFFF">
<%
Collection c = new ArrayList();
Man man1 = new Man();
man1.setName("diego");
c.add(man1);
Man man2 = new Man();
man2.setName("Zidane");
c.add(man2);
Man man3 = new Man();
man3.setName("Rui");
c.add(man3);
People p =new People();
p.setMen(c);
request.setAttribute("people",p);
%>
Test loop tag:
<br>
<diego:withObject value="${people}">
<diego:withCollection property="men">
<diego:elementout property="name"/>
<br>
</diego:withCollection>
</diego:withObject>
</body>
</html>
運行,則可以看到:
Test loop tag:
diego
Zidane
Rui
=======================================================================
Taglib原理和實現 第五章:再論支持El表達式和jstl標簽
1.問題:你想和jstl共同工作。比如,在用自己的標簽處理一些邏輯之后,讓jstl處理余下的工作。
2。看這個jsp例子:
....
<%
String name="diego";
request.setAttribute("name",name);
%>
<c:out value="${name}"/>
......
許多jstl標簽支持El表達式,所以,只要你在自己的標簽內部把值塞進request,其他jstl標簽就能使用它們
3。下面這個例子,從request里面取得對象,找到它屬性的值,塞到request里去。
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 = null;
private String property = null;
private String var = null;
public void setVar(String var)
{
this.var = var;
}
public void setProperty(String property)
{
this.property = property;
}
public void setValue(Object value)throws JspException{
this.value = ExpressionEvaluatorManager.evaluate(
"value", value.toString(), Object.class, this, pageContext);
}
public int doEndTag() throws JspException{
Object propertyValue = null;
try{
propertyValue = PropertyUtils.getProperty(value, property);
}
catch (Exception e) {
throw new JspException(e);
}
pageContext.setAttribute(var,propertyValue);
return EVAL_PAGE;
}
}
編寫tld
<!--SetVarTag-->
<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>
</attribute>
<attribute>
<name>property</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
<attribute>
<name>var</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
</tag>
編寫jsp
<%@ page language="java" %>
<%@ page import="diegoyun.vo.*"%>
<%@ taglib uri="/WEB-INF/tlds/diego.tld" prefix="diego"%>
<%@ taglib uri="/WEB-INF/tlds/c.tld" prefix="c"%>
<html>
<body bgcolor="#FFFFFF">
<%
Man man = new Man();
man.setName("diego");
request.setAttribute("man",man);
%>
Get value from request and set it's property value into request:<br>
<diego:set value="${man}" property="name" var="myname"/>
now use OutTag of jstl taglib to get the name:<br>
value is : <c:out value="${myname}" />
</body>
</html>
運行,效果如下:
Get value from request and set it's property value into request:
now use OutTag of jstl taglib to get the name:
value is : diego
4。結語。和jstl交互是非常有用的技術。在jstl里提供了許多完成基本功能的標簽,如輸出,循環,條件選擇等。僅在處理自己特定邏輯的時候才實現自己的標簽,并提供和jstl交互,能大大提高重用性和減少工作量
========================================================
Taglib原理和實現 第六章:標簽內常用方法總結
1。支持el表達式:
import org.apache.taglibs.standard.lang.support.ExpressionEvaluatorManager;
private Object value = null;
this.value = ExpressionEvaluatorManager.evaluate("value", value.toString(), Object.class, this, pageContext);
2.用BeanUtil取屬性值
import org.apache.commons.beanutils.PropertyUtils;
private String property=null;
Object propertyValue = PropertyUtils.getProperty(value, property);
3.設置request里的值
pageContext.setAttribute("var",propertyValue);
4。打印
pageContext.getOut().print(outputString);
5。取得父標簽,取得想要的標簽,即使它非父
getParent()
findAncestorWithClass(this,ancestorTag.class);
6。標簽自帶方法和常量,方法按照容器的調用順序排列。示例
<c:if test="...">
<c:out value="..."/>
</c:if>
doStartTag : 容器解析到c:if左尖括號(“<”)時調用
doInitBody : 容器解析到c:if右尖括號(“>”)和c:out左尖括號(“<”)時調用
doAfterBody : 容器解析到c:out結束標記(“/>”)時調用
doEndTag :容器解析到c:if結束標記(“/>”)時調用
EVAL_BODY_SKIP : 通常在 doStartTag 方法里調用,忽略標簽包括的內容,假如返回這個值,上面的c:if忽略c:out
EVAL_BODY_INCLUDE :通常在 doAfterBody 方法里調用,再次執行body,假如返回這個值,上面的c:out被執行多次
EVAL_PAGE :可在任何方法里調用。返回jsp頁面