一、引入標籤
其實在JSP中我們已經使用過了幾個標籤,比如<jsp:forward>、<jsp:include>等;這些標籤都是預先定義好的,如果我們需要自訂標籤,就需要學習標籤編程。
標籤編程的優點就是靈活性+封裝性。
靈活性體現在屬性的賦值上,可以賦任意值。比如<jsp:forward page="A"> //A處可以賦任意的URL;
封裝性體現在內部的行為的封裝,因為標籤是用一個類實現的,因此類的方法中能夠包含任意複雜的動作。
因此標籤是為了能夠在JSP中使用儘可能少的Scriptlet;
比如:
<table> <% for(int i=0;i<10;i++){ %> <tr> <% for(int j=0;j<10;j++){ %> <td><%=i*j%></td> <% } %> </tr> <% } %> </table>
這個代碼非常混亂,但是如果通過標籤,從這段代碼都封裝在一個標籤裡,則代碼就清晰了很多。
接下來是我通過標籤封裝後的JSP頁面效果;
<%@ page contentType="text/html" pageEncoding="GBK"%><%@ taglib prefix="xiazdong" uri="xiazdong"%><html><head><title></title></head><body><xiazdong:table row="5" col="3"/></body></html>
是不是大大縮短了呢??再來看看我在背後幹了什麼。。。。
TableTagSupport.java
package org.tagext;import javax.servlet.jsp.tagext.*;import javax.servlet.jsp.*;public class TableTagSupport extends TagSupport{private String row;private String col;public String getRow(){return row;}public String getCol(){return col;}public void setRow(String row){this.row = row;}public void setCol(String col){this.col = col;}public int doStartTag()throws JspException{JspWriter out = super.pageContext.getOut();try{out.println("<table border=\"3\"> ");for(int i=0;i<Integer.parseInt(row);i++){out.println("<tr>");for(int j=0;j<Integer.parseInt(col);j++){out.println("<td>"+i*j+"</td>");}out.println("</tr>");}out.println("</table>");}catch(Exception e){}return TagSupport.SKIP_BODY;}}
是不是很神奇。。。那接下來就看看實現的過程吧。
注意,在編寫標籤之前,必須把tomcat\lib中的jsp-api.jar檔案配置在CLASSPATH中才可以;
二、基本標籤編寫
1、TagSupport類
如果要編寫一個標籤類,則必須繼承javax.servlet.jsp.tagext.TagSupport;
TagSupport中提供了很多常用方法:
(1) public int doStartTag()throws JspException; // 標籤開始時調用
能夠返回SKIP_BODY(跳過標籤體)、EVAL_BODY_INCLUDE(執列標籤體)
(2) public int doEndTag()throws JspException; // 標籤結束時調用
能夠返回SKIP_PAGE(立刻停止執行)、EVAL_PAGE(JSP正常運行完畢);
(3) int SKIP_BODY; // 跳過標籤體
(4) int EVAL_BODY_INCLUDE; //執列標籤體
(5) int EVAL_BODY_AGAIN; // 重複執列標籤體,主要是因為集合迭代輸出,只能在doAfterBody中使用;
(6) public int doAfterBody()throws JspException; // 執行完一次標籤體後調用的函數;
能夠返回SKIP_BODY(結束標籤體)、EVAL_BODY_AGAIN(重複執列標籤體)
(7) JspWriter out = super.pageContext.getOut();//擷取向網頁輸出的輸出資料流;
舉例講述這些函數和常量代表什麼意思:
<xiazdong:hello> <!--標籤頭 --> <h3><%="xiazdong"%></h3> <!-- 標籤體--></xiazdong:hello> <!-- 標籤尾-->
執行流程如下:
(1)doStartTag(); <xiazdong:hello>時調用,如果是EVAL_BODY_INCLUDE,則繼續;如果是SKIP_BODY,則執行(4)
(2)執列標籤體;
(3)如果實現了doAfterBody,則執行;如果返回SKIP_BODY,則執行(4) ; 如果返回EVAL_BODY_AGAIN,則重複執行doAfterBody;
(4)doEndTag():標籤尾調用;
2.製作無屬性標籤
為了清晰,我們以例子說明。
代碼舉例1:實現顯示Hello world;
1.編寫HelloTag.java
package org.tagext;import javax.servlet.jsp.tagext.*;import javax.servlet.jsp.*;public class HelloTag extends TagSupport{public int doStartTag()throws JspException{JspWriter out = super.pageContext.getOut();try{out.println("<h3>Hello world!!!</h3>");}catch(Exception e){}return TagSupport.SKIP_BODY;}}
注意點:
(1)javax.servlet.jsp.tagext.TagSupport,必須記住;
(2)JspWriter out = super.pageContext.getOut();
(3)public int doStartTag()throws JspException;必須記住;
2.編寫xiazdong.tld
tld檔案是標籤描述檔案,是整個標籤編程的核心,用來描述自訂的標籤的名字、標籤的實作類別、是否有標籤體、描述屬性等;
一個tld檔案其實類似於一個標籤庫,裡面能夠描述很多標籤;每個標籤都以
<tag><name>hello</name><!--表示標籤的名字 類似於<jsp:forward>中的forward--><tag-class>org.tagext.HelloTag</tag-class><!--標籤所在類 --><body-content>empty</body-content><!--是否存在標籤體--></tag>
的形式描述;
*.tld檔案的模版如下:(因為tld檔案前面這些內容都不是很重要,因此可以直接複製黏貼)
<?xml version="1.0" encoding="UTF-8" ?><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.0</tlib-version>//插入tag描述</taglib>
3.在web.xml中編寫映射
編寫映射的目的類似於給一個網頁配置一個映射地址,為了方便訪問;
比如本來需要訪問/a/b/c/d/e/f/g/1.tld,這個路徑非常麻煩,但是如果通過映射,我們可以只需要用"a"表示一長串的檔案路徑及名稱;
<jsp-config><taglib><taglib-uri>xiazdong</taglib-uri><taglib-location>/WEB-INF/classes/xiazdong.tld</taglib-location></taglib></jsp-config>
4.編寫JSP頁面並使用自訂的標籤
<%@ taglib prefix="" uri=""%> prefix類似於<jsp:forward>中的jsp,uri中使用前面所說的映射;
<%@ page contentType="text/html" pageEncoding="GBK"%><%@ taglib prefix="xiazdong" uri="xiazdong"%><html><head><title></title></head><body><h1><xiazdong:hello/></h1></body></html>
3.製作有屬性標籤
其實製作有屬性和製作無屬性沒有太大區別,
1.需要在原來的HelloTag.java 中加入屬性.
如果做形如:<xiazdong:hello id="" format="" />的屬性,則需要在HelloTag.java中添加兩個屬性,id屬性和format屬性;
形如:
class HelloTag extends TagSupport{private String id;private String format;//setter和getterpublic int doStartTag()throws JspException{}}
在使用者使用標籤,並為id和format屬性賦值時,會自動調用setter方法,將HelloTag類中的id和format賦值;
2.在tld檔案中添加屬性描述:
屬性描述形如:
<body-content>empty</body-content>後面加上<attribute><name>name</name><!--屬性姓名 --><required>true</required><!--屬性是否必要 --><rtexprvalue>empty</rtexprvalue><!--是否支援運算式語言 --></attribute>
代碼執行個體:
HelloTag.java
package org.tagext;import javax.servlet.jsp.tagext.*;import javax.servlet.jsp.*;public class HelloTag extends TagSupport{private String name;private String age;public String getName(){return name;}public String getAge(){return age;}public void setName(String name){this.name = name;}public void setAge(String age){this.age = age;}public int doStartTag()throws JspException{JspWriter out = super.pageContext.getOut();try{out.println("<h3>姓名:"+name+"</h3>");out.println("<h3>年齡:"+age+"</h3>");}catch(Exception e){}return TagSupport.SKIP_BODY;}}
xiazdong.tld 需要加上attribute的描述:名字、是否必須、是否支援運算式語言
<?xml version="1.0" encoding="UTF-8" ?><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.0</tlib-version> <short-name>x</short-name> <tag><name>hello</name><tag-class>org.tagext.HelloTag</tag-class><body-content>empty</body-content><attribute><name>name</name><required>true</required><rtexprvalue>empty</rtexprvalue></attribute><attribute><name>age</name><required>true</required><rtexprvalue>empty</rtexprvalue></attribute> </tag></taglib>
HelloTag.jsp
<%@ page contentType="text/html" pageEncoding="GBK"%><%@ taglib prefix="xiazdong" uri="xiazdong"%><html><head><title></title></head><body><h1><xiazdong:hello name="xiazdong" age="20"/></h1></body></html>
三、IterationTag和Tag介面的區別
Tag介面只有一些最基本的標籤編程方法,而IterationTag介面是用於迭代輸出,比如EVAL_BODY_AGAIN等;