使用Java來開發JSP標記
最近復習JSP中。我們知道JSP是Java WEB的一個規范。在這個規范之內我們可以自己去做很多事情。JSP API提供了接口允許我們自己去自定義JSP標簽和EL表達式。自定義JSP標簽的大致思想是這樣的,實現一個包含標簽參數和行為邏輯的類,這個類我們稱它為標記處理器類,它實質上是一個Bean,我們設置標簽的屬性實際上是調用它的setter方法。接下來通過使用標記庫描述文件(TLD)來描述標簽的相關信息,以便在JSP頁面中使用taglib指令時能順利的找到它。下面我們來看看詳細應該怎么去做。
從JSP2.0開始引入了一個簡單標記處理器接口——javax.servlet.jsp.tagext.SimpleTag接口,這個接口取代了原先JSP1.0提供的3個標記處理器接口,我們只需要使用SimpleTarget接口就可以實現所有類型的JSP標記。之所以稱之為簡單,是因為這樣設計讓開發者在編寫程序上省了不少力氣,但是它可以實現很復雜的功能,并不是說它的功能很簡單。
該接口包含了如下5個方法:
void doTag(); //該方法包含了標簽執行的業務邏輯
JspTag getParent(); //獲取該標簽的父標簽
setJspBody(JspFragment body); //設置JSP標簽體,關于JspFragment類我們稍后再討論
setJspContext(JspContext ctx);//設置JSP上下文
setParent(JspTag parent); //設置父標簽
但是多數情況下我們不需要去親自實現這個接口,因為作為開發者,我們關心的是業務邏輯,也就是doTag()方法的內容,這種情況我們只需要繼承javax.servlet.jsp.tagext.SimpleTagSupport類即可,這個類為我們實現了SimpleTag接口,我們只需要去重寫它的doTag()方法。下面我們來實現一個最簡單的標簽:給標簽傳遞一個參數,讓它在頁面上顯示出來.
首先我們要做的是寫一個標記處理器類:
package test.jsp.tag.simple;
import java.io.IOException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class HelloTag extends SimpleTagSupport{
private String name;
public void setName(String name){
this.name = name;
}
@Override
public void doTag()throws IOException{
this.getJspContext().getOut().print("Hello! "+name);
}
}
嗯,這是一個繼承了SimpleTagSupport類的Bean,一樣擁有屬性、setter方法。唯一特別的是重寫了父類的doTag()方法。在這個doTag()中,我們通過獲得一個JSP上下文對象來向頁面輸出一句話:Hello!XXX。
下面是該介紹JspContext對象的時候了,這是一個抽象類,它唯一的子類是PageContext。這么說剛才所返回的實際對象是PageContext類型的。我們可以對其進行強制轉換:PageContext pageCtx = (PageContext)this.getJspContext();
通過PageContext對象我們就可以訪問到Request和Response對象了,如:
HttpServletRequest request = (HttpServletRequest)pageCtx.getRequest();
HttpServletResponse response = (HttpServletResponse)pageCtx.getResponse();
通過這種方式我們就可以在標簽處理器中操作WEB應用各個作用域的數據了。
我們都知道在使用JSP標簽時要在JSP頁面開頭使用taglib指令進行聲明,如:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
taglib指令通過URL來對標簽進行定位。但是它是如何做到的呢?這就要靠TLD了,TLD就是標記庫描述文件(Tag Library Descriptor,TLD),我們看JSTL的JAR包中,META-INF文件夾下每個庫都有一個對應的部署描述文件。TLD實際上是一個XML文件,里面描述了標記庫和其中標記的信息。一個完整的TLD文件結構如下所示:
<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 http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
version="2.0">
<tlib-version>1.1</tlib-version>
<short-name>test</short-name>
<uri>http://test</uri>
<tag>
<name>hello</name>
<tag-class>test.jsp.tag.simple.HelloTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>name</name>
<required>true</required>
</attribute>
</tag>
<tag>
<name>if</name>
<tag-class>test.jsp.tag.simple.IfTag</tag-class>
<body-content>scriptless</body-content>
<attribute>
<name>test</name>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<tag>
<name>foreach</name>
<tag-class>test.jsp.tag.simple.ForeachTag</tag-class>
<body-content>scriptless</body-content>
<attribute>
<name>items</name>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>var</name>
</attribute>
</tag>
<function>
<description>
Just for test
</description>
<name>add</name>
<function-class>test.jsp.el.AddEl</function-class>
<function-signature>int add(int,int)</function-signature>
</function>
</taglib>
起始標簽是taglib,<tlib-version>定義了taglib的版本號,<short-name>定義了標簽庫的簡稱,<uri>定義了URI標識,taglib就是通過此URI來找到相關的標記庫的。
接下來的<tag>標簽便是定義JSP標簽的屬性了:
以下是必須的屬性
<name>標簽名稱
<tag-class>標記器類
<body-content>動作體類型,JSP2.0支持如下動作體:
empty 空標記
jsp:可以包含標簽、EL和Scriptlet
scriptless:不允許Java腳本內容
tagdependent:對體不進行處理
<attribute>標簽定義了標簽參數。
其中:
<name>標簽指定了參數的名稱。
<required>指定了參數是否是必要的。true是必要,false是不必要
<rtexprvalue>指定了是否允許使用表達式(包括EL表達式和Java表達式),true為允許,false為不允許
<funtion>標簽定義了EL表達式,我們稍后再做介紹。
如何執行動作體?
我們在編寫JSP頁面時經常會用到<c:if>和<c:forEach>之類的標簽
<c:if test="true">
<p>條件是真的,執行!</p>
</c:if>
只要滿足條件,就會去執行動作體,那么我們怎么在自定義JSP標記中去執行動作體呢?
首先我們先要來研究一個叫做JspFragment的類。JspFragment代表了JSP標簽的動作體,我們通過它的invoke方法就可以執行動作體,下面我們來實現一個類似于<c:if>的標簽:
package test.jsp.tag.simple;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class IfTag extends SimpleTagSupport{
private boolean test;
public void setTest(boolean test){
this.test = test;
}
@Override
public void doTag()throws IOException,JspException{
JspFragment body = getJspBody();
//如果成立則執行相應邏輯
if(test){
body.invoke(null);
}
}
}
只要滿足了條件,標簽動作體就會執行。
invoke方法接受一個java.io.Writer對象,使用這個對象將內容輸出到頁面,如果不輸出則可以給它傳遞一個null參數。
JSP中引入了EL表達式給我們的開發帶來了很大的方便,同自定義標簽一樣,允許開發者去自定義EL表達式函數,定義EL表達式函數要比定義標簽簡單多了,我們甚至不需要去實現任何接口,假設我們要實現一個簡單的EL表達式函數來計算兩個數的和,就可以這樣:
package test.jsp.tag.simple;
public class Calculator {
public static int add(int a,int b){
return a+b;
}
public static int minus(int a,int b){
return a-b;
}
public static int multiply(int a,int b){
return a*b;
}
public static int divide(int a,int b){
return a/b;
}
}
這個類中全部是static方法,這是一個工具類。
然后我們只需要在TLD聲明即可:
<function>
<description>
Just for test
</description>
<name>add</name>
<function-class>test.jsp.el.AddEl</function-class>
<function-signature>int add(int,int)</function-signature>
</function>
function-signature類似于C語言中的函數聲明,通過這個找到到底調用類中哪個工具方法。
使用的時候只需要這樣就可以了:${test:add(1,2)}運行時輸出結果為3