在JSP中有一種機制,可以讓你在JSP頁面中插入與HTML類似的标記。本文介紹JSP定制标記的基本概念和構成,以及如何開發和應用JSP定制标記。
關鍵字
JSP,XML,TLD,标記符
什麼是标記
使用HTML語言我們可以這樣去編輯我們的網頁:
<HTML>
<HEAD>
<TITLE>
HELLO WORLD
</TITLE>
</HEAD>
<BODY>
</BODY>
</HTML>
在這裡我們把</HEAD>,<TITLE>,<BODY>稱為标記。HTML 标記( HTML Markup)是HTML文檔的控制語言,用于指定浏覽器顯示和列印文檔的方式.它是用小于号"<"和大于号">"括起來的短語和符号,如<Html>、</Body>等。許多HTMl标記以成對的方式出現,如<TITLE></TITLE>、<Body></Body> 等。在JSP中我們也可以定制自己的标記,以供JSP頁面使用,如下例所示
<!—login.jsp-->
<%@ taglib uri="/tlds/taglib.tld" prefix="tagclass" %>
<html>
<head>
<title>login</title>
</head>
<body>
<tagclass:login width="200" height= "100" >
</tagclass:login>
</body>
</html>
在上例中</tagclass:login>就是一個JSP定制标記符。widtht、height是這個标記的屬性。<%@ taglib uri="/tlds/taglib.tld" prefix="tagclass" %>是一個标記庫定義指令,在稍後我們将會讨論。在JSP中定制标記符,實質上就是以标記的形式封裝了一個俱有獨立功能的Java類。标記的使用減少了直接嵌入JSP頁面的Java代碼,友善了頁面的布局,并且有利于代碼的複用,提高了開發的效率。
JSP伺服器解析标記的過程
那麼當一個标記被嵌入JSP頁面後,JSP伺服器是如何對這個标記進行解析的呢?下面讓我們一起看一下它的順序圖:
圖中各對象的含義如下所示:
Client: 表示用戶端。
JSP-Server:JSP伺服器。
JSP-Page:JSP頁面。
TLD: 标記庫描述檔案,定義标記和标記的各種屬性和處理檔案等。
TagClass 标記處理程式
當一個使用者通路一個JSP頁面時,這個請求被發送到JSP伺服器,JSP伺服器會根據這個請求去調用相應的頁面,如果這個頁面中有自定義的标記,JSP服務就會根據頁面指令<%@ taglib>去通路TLD得到處理程式的相關資訊,接着調用該處理程式的構造器方法,啟動标記符處理程式,并讀取标記符的屬性和相應值。對每個沒有設定屬性的,調用相應的set方法。當标記符第一次使用時,它的任何屬性都不會做過設定,是以對每個屬性都調用set方法。屬性設定完以後,JSP伺服器調用處理程式的doStartTag(),然後再調用doEndTag()方法。最後JSP伺服器會繼續處理剩下的頁面,在頁面結尾調用release()方法,清理占用的所有資源。
TLD檔案
TLD(TLD:Tag Library Descriptor标記庫描述符)檔案,标準的XML格式的标記定義檔案,被用來存放标記符的資訊,下面就是一個典型的TLD檔案。
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!—XML的版本及其字元集-->
<!DOCTYPE taglib
PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"
"http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">
<!—文檔類型定義-->
<taglib>
<!—此标記說明我們開始描述一個标記庫-->
<tlibversion>1.0</tlibversion>
<!—标記庫的版本-->
<jspversion>1.1</jspversion>
<!—所使用的JSP的版本-->
<shortname>tagclass</shortname>
<!—預設的名稱-->
<tag>
<name>login</name>
<!—标記的名稱-->
<tagclass>
tagclass.login.login
<!—處理這個Tag的相應的類的名稱-->
</tagclass>
<info>
<!—對本标記符的描述-->
</info>
<attribute>
<!—開始定義标記的屬性-->
<name>height</name>
<!—屬性的名稱-->
<required>true</required>
<!—表示這個屬性是不是必須的-->
<rtexprvalue>true</rtexprvalue>
<!—表示這個屬性是否可以用JSP的程式段的結果輸出-->
</attribute>
<name>width</name>
</tag>
</taglib>
在這個TLD檔案中定義了隻有一個标記符的标記符庫,這個名為login的标記符會調用一個Applet以驗證使用者的合法性。處理這個标記的類就是tagclass.login.login。width、height是這個标記的兩個屬性。屬性是在使用标記符時作為參數發送的值。我們可以在上面的示例中增加幾個标記,也可以為每個标記添加幾個屬性。我們開發标記符庫時不一定非要從頭開始,自己編寫一個全新TLD。我們可以使用某個內建的開發的環境,也可以修改上面的例子。
TagLib指令
那麼當JSP伺服器在解析一個标記符時,它是如何定義一個标記庫的呢?這就是TagLib指令的主要責任。
Taglib 指令
定義一個标記庫以及其自定義标記的字首.
JSP 文法
<%@ taglib uri="URIToTagLibrary" prefix="tagPrefix" %>
例子
描述
<% @ taglib %>指令聲明此JSP檔案使用了自定義的标記,同時引用标記庫,
也指定了他們的标記的字首。 你必須在使用自定義标記之前使用<% @ taglib %>指令。
屬性
uri="URIToTagLibrary" :Uniform Resource Identifier (URI)根據标記的字首對自定義的标記進行唯一的命名,URI可以是一個相對或絕對的路徑。
prefix="tagPrefix":在自定義标記之前的字首。如上例中的</tagclass:login>
标記符的處理程式(Tag handle)
我們還是以一個例子來看下如何實作一個Tag handle。首先是看一下它的類圖:
讓我們再看一下它的代碼:
package tagclass.login;
import javax.servlet.jsp.tagext.TagSupport;
import javax.servlet.jsp.*;
import java.io.*;
public class login extends TagSupport
{
public login()
super();
}
public int doStartTag() throws JspTagException
JspWriter out = pageContext.getOut();
try
out.println("<APPLET CODEBASE=applet/login/ CODE=login.class width=200 height=100 > </APPLET>");
catch(Exception e)
return SKIP_BODY;
publicc int doEndTag()throws JsptagException
return EVAL_PAGE;
public void release()
super.release();
public void setWidth(String language)
this.width = width;
public String getWidth()
return this.width;
public void setHeight(String height)
this.height=height;
public String getHeight()
return this.height;
private String width;
private String height;
從以上我們可以看出,實作一個簡單的标記符處理程式有幾個要求:①增加一個類,使之繼承java.Servlet.jsp.tagext.TagSupport類。這個類提供了java.Servlet.jsp.tagext.Tag接口所要求的所有的方法。另外,還需要使用一些基本的API,使JSP容器能夠調用我們自己提供的标記符處理程式。②必須為每個标記符屬性分别建立一個get<attribute>和set<attribute>方法,JSP容器需要使用這些方法處理程式傳遞參數。③要為标記符處理程式建立一個構造器和自毀器。JSP需要使用構造器啟動處理程式。自毀器是在realease()方法中定義的。在處理程式的生命周期結束時,需要調用自毀器釋放所占用的資源。④建立兩個名為doStartTag()和doEndTag()的方法,執行具體的處理和輸出動作。這兩個方法是在處理自定義标記符的起始位置和結束位置調用的。它們的傳回值是在Tag Interface裡定義的靜态int,這幾個靜态值分别是:
SKIP_BODY隐含0 :跳過了開始和結束标簽之間的代碼。
EVAL_BODY_INCLUDE隐含1:将body的内容輸出到存在的輸出流中
SKIP_PAGE隐含5 : 忽略剩下的頁面。
EVAL_PAGE隐含6:繼續執行下面的頁
當然标記符也有它自己的缺點。很不友善的封裝過程,有限的功能。對于一些不太複雜和功能單一的邏輯描述,需要傳遞的參數要求不高時,使用JSP标記,要友善的多。對于大多數的商業邏輯應用,還是使用bean要好的多,也宜于servlet控制。
附錄:文章中所用示例的完整代碼
JSP代碼:login.jsp
<head>
<title></title>
</head>
<body>
<tagclass:login width="200" height= "100" >
</body>
标記符描述庫:taglib.tld
</info>
标記符處理程式:login.java
标記符處理程式中所使用的Applet : login.java
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
public class login extends Applet implements ActionListener
private String s_username;
private String s_userpassword;
private Button b_ok;
private Button b_register;
private Label l_username;
private Label l_userpassword;
private TextField t_username;
private TextField t_userpassword;
private GridLayout g_gridlayout;
public void init()
b_ok=new Button("ok");
b_register=new Button("register");
l_username= new Label("name");
l_userpassword=new Label("password");
t_username=new TextField();
t_userpassword=new TextField();
b_ok.addActionListener(this);
b_register.addActionListener(this);
g_gridlayout=new GridLayout(3,2,10,10);
this.setLayout(g_gridlayout);
//this.setBackground(Color.blue);
add(l_username);
add(t_username);
add(l_userpassword);
add(t_userpassword);
add(b_ok);
add(b_register);
public void actionPerformed(ActionEvent ev)
String s_label=ev.getActionCommand();
if (s_label.equals("ok"))
t_username.setText("name");
if (s_label.equals("register"))
t_userpassword.setText("password");
public void paint(Graphics g)