自定義標籤庫並非 JSP 2 纔出現的,JSP 1.1 版中已經增長了自定義標籤庫規範,自定義標籤庫是一種很是優秀的表現層組件技術。經過使用自定義標籤庫,能夠在簡單的標籤中封裝複雜的功能。 html
爲何要使用自定義標籤呢?主要是爲了取代醜陋的 JSP 腳本。在 HTML 頁面中插入 JSP 腳本有以下幾個壞處: java
出於以上三點的考慮,咱們須要一種可在頁面中使用的標籤,這種標籤具備和 HTML 標籤相似的語法,但由能夠完成 JSP 腳本的功能——這種標籤就是 JSP 自定義標籤。 mysql
在 JSP1.1 規範中開發自定義標籤庫比較複雜,JSP 2 規範簡化了標籤庫的開發,在 JSP 2 中開發標籤庫只需以下幾個步驟: 程序員
當咱們在 JSP 頁面使用一個簡單的標籤時,底層實際上由標籤處理類提供支持,從而可使用簡單的標籤來封裝複雜的功能,從而使團隊更好地協做開發(能讓美工人員更好地參與 JSP 頁面的開發)。 數據庫
早期 JSP 自定義標籤類開發過程略微複雜一些,但 JSP 2 已經簡化了這個過程,它只要自定義標籤類都必須繼承一個父類:javax.servlet.jsp.tagext.SimpleTagSupport,除此以外,JSP 自定義標籤類還有以下要求。 瀏覽器
下面開發一個最簡單的自定義標籤,該標籤負責在頁面上輸出 HelloWorld。 app
// 標籤處理類,繼承 SimpleTagSupport 父類 public class HelloWorldTag extends SimpleTagSupport { // 重寫 doTag 方法,該方法在標籤結束生成頁面內容 public void doTag()throws JspException, IOException { // 獲取頁面輸出流,並輸出字符串 getJspContext().getOut().write("Hello World"); } } |
上面這個標籤處理類很是簡單,它繼承了 SimpleTagSupport 父類,並重寫 doTag() 方法,而 doTag() 方法則負責輸出頁面內容。該標籤沒有屬性,所以無須提供 setter 和 getter 方法。 框架
TLD 是 Tag Library Definition 的縮寫,即標籤庫定義,文件的後綴是 tld,每一個 TLD 文件對應一個標籤庫,一個標籤庫中可包含多個標籤,TLD 文件也稱爲標籤庫定義文件。
標籤庫定義文件的根元素是 taglib,它能夠包含多個 tag 子元素,每一個 tag 子元素都定義一個標籤。一般咱們能夠到 Web 容器下複製一個標籤庫定義文件,並在此基礎上進行修改便可。例如 Tomcat6.0,在 webapps\examples\WEB-INF\jsp2 路徑下包含了一個 jsp2-example-taglib.tld 文件,這就是示範用的標籤庫定義文件。
將該文件複製到 Web 應用的 WEB-INF/ 路徑,或 WEB-INF 的任意子路徑下,並對該文件進行簡單修改,修改後的 mytaglib.tld 文件代碼以下:
<?xml version="1.0" encoding="GBK"?> <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 web-jsptaglibrary_2_0.xsd" version="2.0"> <tlib-version>1.0</tlib-version> <short-name>mytaglib</short-name> <!-- 定義該標籤庫的URI --> <uri>http://www.crazyit.org/mytaglib</uri> <!-- 定義第一個標籤 --> <tag> <!-- 定義標籤名 --> <name>helloWorld</name> <!-- 定義標籤處理類 --> <tag-class>lee.HelloWorldTag</tag-class> <!-- 定義標籤體爲空 --> <body-content>empty</body-content> </tag> </taglib> |
上面標籤庫定義文件也是一個標準的 XML 文件,該 XML 文件的根元素是 taglib 元素,所以咱們每次編寫標籤庫定義文件都直接添加該元素便可。
taglib 下有三個子元素:
除此以外,taglib 元素下能夠包含多個 tag 元素,每一個 tag 元素定義一個標籤,tag 元素下至少應包含以下三個子元素:
實際上因爲 JSP 2 規範再也不推薦使用 JSP 腳本,因此 JSP 2 自定義標籤的標籤體中不能包含 JSP 腳本。因此實際上 body-content 元素的值不能夠是 JSP。
定義了上面的標籤庫定義文件後,將標籤庫文件放在 Web 應用的 WEB-INF 路徑,或任意子路徑下,Java Web 規範會自動加載該文件,則該文件定義的標籤庫也將生效。
在 JSP 頁面中肯定指定標籤須要 2 點:
使用標籤庫分紅如下兩個步驟:
taglib 的語法格式以下:
<%@ taglib uri="tagliburi" prefix="tagPrefix" %> |
其中 uri 屬性肯定標籤庫的 URI,這個 URI 能夠肯定一個標籤庫。而 prefix 屬性指定標籤庫前綴,即全部使用該前綴的標籤將由此標籤庫處理。
使用標籤的語法格式以下:
<tagPrefix:tagName tagAttribute=」tagValue」 … > <tagBody/> </tagPrefix:tagName> |
若是該標籤沒有標籤體,則可使用以下語法格式:
<tagPrefix:tagName tagAttribute=」tagValue」 … /> |
上面使用標籤的語法裏都包含了設置屬性值,前面咱們介紹的 HelloWorldTag 標籤沒有任何屬性,因此使用該標籤只需用 <mytag:helloWorld/> 便可。其中 mytag 是 taglib 指令爲標籤庫指定的前綴,而 helloWorld 是標籤名。
下面是使用 helloWorld 標籤的 JSP 頁面代碼:
<%@ page contentType="text/html; charset=GBK"%> <!-- 導入標籤庫,指定mytag前綴的標籤, 由http://www.crazyit.org/mytaglib的標籤庫處理 --> <%@ taglib uri="http://www.crazyit.org/mytaglib" prefix="mytag"%> <html> <head> <title>自定義標籤示範</title> </head> <body bgcolor="#ffffc0"> <h2>下面顯示的是自定義標籤中的內容</h2> <!-- 使用標籤 ,其中mytag是標籤前綴,根據taglib的編譯指令, mytag前綴將由http://www.crazyit.org/mytaglib的標籤庫處理 --> <mytag:helloWorld/><BR> </body> </html> |
上面頁面中第一行粗體字代碼指定了 http://www.crazyit.org/mytaglib 標籤庫的前綴爲 mytag,第二行粗體字代碼代表使用 mytag 前綴對應標籤庫裏的 helloWorld 標籤。瀏覽該頁面將看到如圖 1 所示效果:
前面的簡單標籤既沒有屬性,也沒有標籤體,用法、功能都比較簡單。實際上還有以下兩種經常使用的標籤:
正如前面介紹的,帶屬性標籤必須爲每一個屬性提供對應的 setter 和 getter 方法。帶屬性標籤的配置方法與簡單標籤也略有差異,下面介紹一個帶屬性標籤的示例:
public class QueryTag extends SimpleTagSupport { //標籤的屬性 private String driver; private String url; private String user; private String pass; private String sql; //執行數據庫訪問的對象 private Connection conn = null; private Statement stmt = null; private ResultSet rs = null; private ResultSetMetaData rsmd = null; //標籤屬性driver的setter方法 public void setDriver(String driver) { this.driver = driver; } //標籤屬性url的setter方法 public void setUrl(String url) { this.url = url; } //標籤屬性user的setter方法 public void setUser(String user) { this.user = user; } //標籤屬性pass的setter方法 public void setPass(String pass) { this.pass = pass; } //標籤屬性driver的getter方法 public String getDriver() { return (this.driver); } //標籤屬性url的getter方法 public String getUrl() { return (this.url); } //標籤屬性user的getter方法 public String getUser() { return (this.user); } //標籤屬性pass的getter方法 public String getPass() { return (this.pass); } //標籤屬性sql的getter方法 public String getSql() { return (this.sql); } //標籤屬性sql的setter方法 public void setSql(String sql) { this.sql = sql; } public void doTag()throws JspException, IOException { try { //註冊驅動 Class.forName(driver); //獲取數據庫鏈接 conn = DriverManager.getConnection(url,user,pass); //建立Statement對象 stmt = conn.createStatement(); //執行查詢 rs = stmt.executeQuery(sql); rsmd = rs.getMetaData(); //獲取列數目 int columnCount = rsmd.getColumnCount(); //獲取頁面輸出流 Writer out = getJspContext().getOut(); //在頁面輸出表格 out.write("<table border='1' bgColor='9999cc' width='400'>"); //遍歷結果集 while (rs.next()) { out.write("<tr>"); //逐列輸出查詢到的數據 for (int i = 1 ; i <= columnCount ; i++ ) { out.write("<td>"); out.write(rs.getString(i)); out.write("</td>"); } out.write("</tr>"); } } catch(ClassNotFoundException cnfe) { cnfe.printStackTrace(); throw new JspException("自定義標籤錯誤" + cnfe.getMessage()); } catch (SQLException ex) { ex.printStackTrace(); throw new JspException("自定義標籤錯誤" + ex.getMessage()); } finally { //關閉結果集 try { if (rs != null) rs.close(); if (stmt != null) stmt.close(); if (conn != null) conn.close(); } catch (SQLException sqle) { sqle.printStackTrace(); } } } } |
上面這個標籤稍微複雜一點,它包含了 5 個屬性,如程序中粗體字代碼所示,則程序須要爲這 5 個屬性提供 setter 和 getter 方法。
該標籤輸出的內容依然由 doTag() 方法決定,該方法會根據 SQL 語句查詢數據庫,並將查詢結果顯示在當前頁面中。
對於有屬性的標籤,須要爲 tag 元素增長 attribute 子元素,每一個 attribute 子元素定義一個屬性,attribue 子元素一般還須要指定以下幾個子元素:
爲了配置上面的 QueryTag 標籤,咱們須要在 mytaglib.tld 文件中增長以下配置片斷:
<!-- 定義第二個標籤 --> <tag> <!-- 定義標籤名 --> <name>query</name> <!-- 定義標籤處理類 --> <tag-class>lee.QueryTag</tag-class> <!-- 定義標籤體爲空 --> <body-content>empty</body-content> <!-- 配置標籤屬性:driver --> <attribute> <name>driver</name> <required>true</required> <fragment>true</fragment> </attribute> <!-- 配置標籤屬性:url --> <attribute> <name>url</name> <required>true</required> <fragment>true</fragment> </attribute> <!-- 配置標籤屬性:user --> <attribute> <name>user</name> <required>true</required> <fragment>true</fragment> </attribute> <!-- 配置標籤屬性:pass --> <attribute> <name>pass</name> <required>true</required> <fragment>true</fragment> </attribute> <!-- 配置標籤屬性:sql --> <attribute> <name>sql</name> <required>true</required> <fragment>true</fragment> </attribute> </tag> |
上面 5 行粗體字代碼分別爲該標籤配置了 driver、url、user、pass 和 sql 等 5 個屬性,並指定這 5 個屬性都是必填屬性、並且屬性值支持動態內容。
配置完畢後,就可在頁面中使用標籤,先導入標籤庫,而後使用標籤。使用標籤的 JSP 頁面片斷以下:
<!-- 導入標籤庫,指定mytag前綴的標籤, 由http://www.crazyit.org/mytaglib的標籤庫處理 --> <%@ taglib uri="http://www.crazyit.org/mytaglib" prefix="mytag"%> ... <!-- 其餘HTML內容 --> <!-- 使用標籤 ,其中mytag是標籤前綴,根據taglib的編譯指令, mytag前綴將由http://www.crazyit.org/mytaglib的標籤庫處理 --> <mytag:query driver="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/javaee" user="root" pass="32147" sql="select * from newsinf"/> |
在瀏覽器中瀏覽該頁面,效果如圖 2 所示。
圖 2 中看到從數據庫裏查詢到 2 條記錄,固然這也須要底層 javaee 數據庫裏包含 newsinf 數據表,且該數據表裏包含這兩條記錄才行。
在 JSP 頁面中只須要使用簡單的標籤,便可完成「複雜」的功能:執行數據庫查詢,並將查詢結果在頁面上以表格形式顯示。這也正是自定義標籤庫的目的——以簡單的標籤,隱藏複雜的邏輯。
固然,並不推薦在標籤處理類中訪問數據庫,由於標籤庫是表現層組件,它不該該包含任何業務邏輯實現代碼,更不該該執行數據庫訪問,它只應該負責顯示邏輯。
帶標籤體的標籤,能夠在標籤內嵌入其餘內容(包括靜態的 HTML 內容和動態的 JSP 內容),一般用於完成一些邏輯運算,例如判斷和循環等。下面以一個迭代器標籤爲示例,介紹帶標籤體標籤的開發過程。
同樣先定義一個標籤處理類,該標籤處理類的代碼以下:
public class IteratorTag extends SimpleTagSupport { //標籤屬性,用於指定須要被迭代的集合 private String collection; //標籤屬性,指定迭代集合元素,爲集合元素指定的名稱 private String item; //collection屬性的setter和getter方法 public void setCollection(String collection) { this.collection = collection; } public String getCollection() { return this.collection; } //item屬性的setter和getter方法 public void setItem(String item) { this.item = item; } public String getItem() { return this.item; } //標籤的處理方法,簡單標籤處理類只須要重寫doTag方法 public void doTag() throws JspException, IOException { //從page scope中獲取屬性名爲collection的集合 Collection itemList = (Collection)getJspContext(). getAttribute(collection); //遍歷集合 for (Object s : itemList) { //將集合的元素設置到page 範圍 getJspContext().setAttribute(item, s ); //輸出標籤體 getJspBody().invoke(null); } } } |
上面標籤處理類與前面處理類並無太大的不一樣,該處理類包含 2 個屬性,併爲這兩個屬性提供了 setter 和 getter 方法。標籤處理類的 doTag 方法首先從 page 範圍內獲取了指定名稱的 Collection 對象,而後遍歷 Collection 對象的元素,每次遍歷都調用了 getJspBody() 方法,如程序中粗體字代碼所示,該方法返回該標籤所包含的標籤體:JspFragment 對象,執行該對象的 invoke() 方法,便可輸出標籤體內容。該標籤的做用是:遍歷指定集合,每遍歷一個集合元素,即輸出標籤體一次。
由於該標籤的標籤體不爲空,配置該標籤時指定 body-content 爲 scriptless,該標籤的配置代碼片斷以下代碼所示:
<!-- 定義第三個標籤 --> <tag> <!-- 定義標籤名 --> <name>iterator</name> <!-- 定義標籤處理類 --> <tag-class>lee.IteratorTag</tag-class> <!-- 定義標籤體支持JSP腳本 --> <body-content>scriptless</body-content> <!-- 配置標籤屬性:collection --> <attribute> <name>collection</name> <required>true</required> <fragment>true</fragment> </attribute> <!-- 配置標籤屬性:item --> <attribute> <name>item</name> <required>true</required> <fragment>true</fragment> </attribute> </tag> |
上面配置片斷中粗體字代碼指定該標籤的標籤體能夠是靜態 HTML 內容,也能夠是表達式語言。
爲了測試在 JSP 頁面中使用該標籤的效果,咱們首先把一個 List 對象設置成 page 範圍的屬性,而後使用該標籤來迭代輸出 List 集合的所有元素。
JSP 頁面代碼以下:
<%@ page import="java.util.*"%> <%@ page contentType="text/html; charset=GBK"%> <!-- 導入標籤庫,指定mytag前綴的標籤, 由http://www.crazyit.org/mytaglib的標籤庫處理 --> <%@ taglib uri="http://www.crazyit.org/mytaglib" prefix="mytag"%> <html> <head> <title>帶標籤體的標籤-迭代器標籤</title> </head> <body> <h2>帶標籤體的標籤-迭代器標籤</h2> <hr> <% //建立一個List對象 List<String> a = new ArrayList<String>(); a.add("hello"); a.add("world"); a.add("java"); //將List對象放入page範圍內 pageContext.setAttribute("a" , a); %> <table border="1" bgcolor="aaaadd" width="300"> <!-- 使用迭代器標籤,對a集合進行迭代 --> <mytag:iterator collection="a" item="item"> <tr> <td>${pageScope.item}</td> <tr> </mytag:iterator> </table> </body> </html> |
上面頁面代碼中粗體字代碼便可實現經過 iterator 標籤來遍歷指定集合,瀏覽該頁面即看到如圖 3 所示界面:
圖 3 顯示了使用 iterator 標籤遍歷集合元素的效果,從 iteratorTag.jsp 頁面的代碼來看,使用 iterator 標籤遍歷集合元素比使用 JSP 腳本遍歷集合元素要優雅得多,這就是自定義標籤的魅力。
實際上 JSTL 標籤庫提供了一套功能很是強大標籤,例如普通的輸出標籤,像咱們剛剛介紹的迭代器標籤,還有用於分支判斷的標籤等等,JSTL(JSP 標準標籤庫)都有很是完善的實現。除此以外,Apache 下還有一套 DisplayTags 的標籤庫實現,作得也很是不錯