JSP自定義標籤


在本章中,我們將討論JSP中的自定義標籤。自定義標籤是使用者定義的JSP語言元素。當包含自定義標籤的JSP頁面被轉換成一個servlet時,標籤被轉換為一個名為標籤處理程式的物件的操作。 然後,Web容器在執行JSP頁面的servlet時呼叫這些操作。

JSP標籤擴充套件允許建立可以直接插入JSP的新標籤。JSP 2.0規範引入了用於編寫這些自定義標籤的簡單標籤處理程式。

要編寫自定義標籤,可以簡單地擴充套件SimpleTagSupport類並覆蓋doTag()方法,可以在這個方法中放置程式碼來為標籤生成內容。

建立標籤範例

為了更好的演示如何使用JSP中的自定義標籤,開啟Eclipse建立一個動態Web專案:CustomTags,其專案結構如下所示 -

假設要定義一個名為<ex:Hello>的自定義標籤,並且希望以下列方式來使用它:

<ex:Hello />

要建立自定義JSP標記,必須首先建立一個充當標記處理程式的Java類。在這個範例中建立一個HelloTag類,如下所示:

檔案:HelloTag.java

package com.yiibai;

import javax.servlet.jsp.tagext.*;
import javax.servlet.jsp.*;
import java.io.*;

public class HelloTag extends SimpleTagSupport {
    public void doTag() throws JspException, IOException {
        JspWriter out = getJspContext().getOut();
        out.println("您好,這是一個自定義標籤內容!");
    }
}

上述程式碼只是簡單的編碼,其中doTag()方法使用getJspContext()方法獲取當前的JspContext物件,並使用它傳送「Hello Custom Tag!」到當前的JspWriter物件

最後,建立以下標籤庫檔案:{webapps}/WEB-INF/custom.tld , 內容如下 -

<taglib>
   <tlib-version>1.0</tlib-version>
   <jsp-version>2.0</jsp-version>
   <short-name>Custom TLD</short-name>
   <tag>
      <name>Hello</name>
      <tag-class>com.yiibai.HelloTag</tag-class>
      <body-content>empty</body-content>
   </tag>
</taglib>

在JSP程式中使用上面定義的自定義標籤Hello,如下所示:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!-- 需要指示定義的宣告檔案 -->
<%@ taglib prefix="ex" uri="WEB-INF/custom.tld"%>
<!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=UTF-8">
<title>自定義標籤範例</title>
</head>
<body>
    <div style="margin: auto; width: 80%">
    <b>以下是自定義標籤輸出的內容:</b><hr/>
    <ex:Hello/>
    </div>

</body>
</html>

呼叫上述JSP,這應該產生以下結果 -

存取標籤體

可以在標籤正文中新增一條訊息,如標準標籤所示。 考慮想定義一個名為<ex:Hello>的自定義標籤,並且使用下面的方式來使用它 -

<ex:Hello>
   This is message body
</ex:Hello>

在上述標籤程式碼中進行以下更改來處理標籤的正文 -

package com.yiibai;

import javax.servlet.jsp.tagext.*;
import javax.servlet.jsp.*;
import java.io.*;

import javax.servlet.jsp.tagext.*;
import javax.servlet.jsp.*;
import java.io.*;

public class HelloTag extends SimpleTagSupport {
    StringWriter sw = new StringWriter();

    public void doTag()

            throws JspException, IOException {
        getJspBody().invoke(sw);
        getJspContext().getOut().println(sw.toString());
    }
}

這裡,由呼叫產生的輸出首先捕獲到一個StringWriter中,然後再寫入與該標籤關聯的JspWriter。需要更改TLD檔案,如下所示:

檔案:custom.tld

<taglib>
   <tlib-version>1.0</tlib-version>
   <jsp-version>2.0</jsp-version>
   <short-name>Example TLD with Body</short-name>

   <tag>
      <name>Hello</name>
      <tag-class>com.yiibai.HelloTag</tag-class>
      <body-content>scriptless</body-content>
   </tag>
</taglib>

現在在正文中呼叫上面的標籤,如下所示:

檔案:index2.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!-- 需要指示定義的宣告檔案 -->
<%@ taglib prefix="ex" uri="WEB-INF/custom.tld"%>
<!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=UTF-8">
<title>自定義標籤範例</title>
</head>
<body>
    <div style="margin: auto; width: 80%">
        <b>以下是自定義標籤輸出的內容:</b>
        <hr />
        <ex:Hello>
        這是標籤體的內容
      </ex:Hello>
    </div>

</body>
</html>

執行上面範例程式碼,將得到以下結果 -

自定義標籤屬性

也可以使用各種屬性以及自定義標籤。要接受屬性值,自定義標籤類需要實現setter方法,與JavaBean setter方法相同,如下所示:

檔案:CTagAttr.java

package com.yiibai;


import javax.servlet.jsp.tagext.*;
import javax.servlet.jsp.*;
import java.io.*;

public class CTagAttr extends SimpleTagSupport {
   private String message;

   public void setMessage(String msg) {
      this.message = msg;
   }
   StringWriter sw = new StringWriter();
   public void doTag()

   throws JspException, IOException {
      if (message != null) {
         /* Use message from attribute */
         JspWriter out = getJspContext().getOut();
         out.println( message );
      } else {
         /* use message from the body */
         getJspBody().invoke(sw);
         getJspContext().getOut().println(sw.toString());
      }
   }
}

屬性的名稱是message,所以setter方法是setMessage()。現在使用<attribute>元素在TLD檔案中新增此屬性,如下所示:

<taglib>
   <tlib-version>1.0</tlib-version>
   <jsp-version>2.0</jsp-version>
   <short-name>Example TLD with Body</short-name>

   <tag>
      <name>Hello</name>
      <tag-class>com.yiibai.CTagAttr</tag-class>
      <body-content>scriptless</body-content>

      <attribute>
         <name>message</name>
      </attribute>

   </tag>
</taglib>

在JSP中使用HelloTag的message屬性如下 -

檔案:ctagattr.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!-- 需要指示定義的宣告檔案 -->
<%@ taglib prefix="ex" uri="WEB-INF/ctagattr.tld"%>
<!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=UTF-8">
<title>自定義標籤屬性範例</title>
</head>
<body>
    <div style="margin: auto; width: 80%">
    <b>以下是自定義標籤輸出的內容:</b><hr/>
    <ex:Hello message = "This is custom tag attribute" />
    </div>

</body>
</html>

這將產生以下結果 -

下面是包括的屬性及說明 -

編號 屬性 描述
1 name name元素定義屬性的名稱。每個屬性名稱對於特定標籤必須是唯一的。
2 required 此規範如果此屬性是必需的或是可選的。則可設定為:false
3 rtexprvalue 宣告tag屬性的執行時表示式值是否有效
4 type 定義此屬性的Java類型別。 預設情況下,它被假定為String型別
5 description 可以提供資訊描述。
6 fragment 宣告此屬性值是否應被視為JspFragment。

以下是指定屬性相關屬性的範例 -

.....
   <attribute>
      <name>attribute_name</name>
      <required>false</required>
      <type>java.util.Date</type>
      <fragment>false</fragment>
   </attribute>
.....

如果使用兩個屬性,則可以按如下所示修改TLD:

.....
   <attribute>
      <name>attribute_name1</name>
      <required>false</required>
      <type>java.util.Boolean</type>
      <fragment>false</fragment>
   </attribute>

   <attribute>
      <name>attribute_name2</name>
      <required>true</required>
      <type>java.util.Date</type>
   </attribute>
.....

JSP自定義標籤屬性運算元據庫範例

在上面幾個範例中,我們已經演示了如何建立一個自定義標籤,列印給定標籤名稱和特定屬性內容。這裡將進一步演示如何在自定義標籤中讀取資料庫表中的資料記錄。假設要使用自定義標籤來顯示最新入職的3名員工,參考以下實現步驟。

建立資料庫和表 -

DROP TABLE IF EXISTS `employees`;
CREATE TABLE `employees` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(64) NOT NULL DEFAULT '',
  `age` int(3) unsigned NOT NULL DEFAULT '0',
  `education` varchar(32) DEFAULT '' COMMENT '學歷',
  `address` varchar(254) DEFAULT NULL,
  `salary` float(8,2) unsigned DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=16 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of employees
-- ----------------------------
INSERT INTO `employees` VALUES ('1', '李家誠', '23', '其它', '海口市人民大道1800號', '8900.00');
INSERT INTO `employees` VALUES ('2', '張輝', '28', '本科', '廣州天河區珠村市場', '19999.99');
INSERT INTO `employees` VALUES ('3', '林賢弟', '29', '博士', '廣州白雲區龍塘村120號', '18990.99');
INSERT INTO `employees` VALUES ('4', '王小簡', '23', '本科', '海口人民大道1688號', '899.98');
INSERT INTO `employees` VALUES ('5', '蔡世傑', '27', '專科', '上海市寶山區聯楊路2211弄26號', '15800.00');

建立一個Java類檔案:Employee.java

package com.yiibai;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.TagSupport;
import java.sql.*;

public class EmployeeTag extends TagSupport {
    private String number;

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    public int doStartTag() throws JspException {
        JspWriter out = pageContext.getOut();
        try {
            Class.forName("com.mysql.jdbc.Driver");
            Connection con = DriverManager.getConnection("jdbc:mysql://localhost/testdb?useSSL=false&characterEncoding=utf8", "root", "123456");
            PreparedStatement ps = con.prepareStatement("SELECT name,education,age FROM `employees` ORDER BY id DESC limit "+this.number);
            //ps.setInt(1, Integer.parseInt(this.number));
            System.out.println("number => "+this.number);
            ResultSet rs = ps.executeQuery();
            if (rs != null) {
                // column name
                out.write("<table border='1'>");
                out.write("<tr>");
                out.write("<th>姓名</th><th>學歷</th><th>年齡</th>");
                out.write("</tr>");
                // column value

                while (rs.next()) {
                    out.write("<tr>");
                    out.write("<td>" + rs.getString("name") + "</td><td>" + rs.getString("education") + "</td><td>" + rs.getString("age") + "</td>");
                    out.write("</tr>");
                }
                out.write("</table>");
            }
            con.close();
        } catch (Exception e) {
            System.out.println(e);
        }
        return SKIP_BODY;
    }
}

檔案:empl.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/j2ee/dtd/web-jsptaglibrary_1_2.dtd">

<taglib>

    <tlib-version>1.2</tlib-version>
    <jsp-version>2.0</jsp-version>
    <short-name>c</short-name>
    <uri>yiibai</uri>

    <tag>
        <name>NewEmpl</name>
        <tag-class>com.yiibai.EmployeeTag</tag-class>
        <attribute>
            <name>number</name>
            <required>true</required>
        </attribute>
    </tag>
</taglib>

檔案:new_empl.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="ex" uri="WEB-INF/empl.tld"%>
<!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=UTF-8">
<title>自定義標籤+資料庫</title>
</head>
<body>
    <center>
        最新加入的3位員工:
        <hr />
        <ex:NewEmpl number="3"/>
    </center>
</body>
</html>

在編寫完成上面範例程式碼後,執行這個JSP檔案,應該會看到以下結果 -