除了JavaMail今日是JavaWeb部分的最后一天了,還有三天的時間是做個小項目。將之前學習的所有知識串起來,準備迎接高級部分。嘿嘿!終于到了高級部分!
今日主要內容是JSP自定義標簽與JSTL,之前有使用過JSTL,十分的方便,還有EL。也有學過自定義EL函數,但個人感覺EL自定義函數沒有JSP的自定義標簽有趣。
一、JSP自定義標簽
我們知道,JSP在被訪問時會被JSP引擎翻譯為Servlet程序,即JSP就是Servlet程序。我們可以在JSP中插入Java代碼,但是在JSP頁面中使用<%...%>嵌入JAVA代碼,使用頁面編寫起來十分混亂,更不利于以后的維護。因為,JSP自定義標簽可以優雅的解決這一問題。JSTL也正是SUN為些開發的一個標簽庫,接下來讓我們來編寫自己的自定義標簽。
定義JSP自定義標簽,需要四個步驟:
1. 編寫一個實現Tag接口的Java類(標簽處理器類),覆蓋其中的doStartTag方法,在doStartTag方法中編寫標簽的功能代碼。
2. 編寫標簽庫描述符(tld)文件,在tld文件中對自定義標簽進行描述。
3. 在WEB應用中部署和安裝自定義標簽庫。
4. 在JSP頁面中導入和使用自定義標簽。
在以前的JSP頁面編寫中,我們有使用過“<cc:if test=”邏輯表達方式”>”和<cc:froEach var=”變量” items=”集合、列表、數組”>。下面我們就來實現與這兩個JSP標簽類似功能的自定義JSP標簽。
1.編寫<cc:if test=”…”>…</cc:forEach>自定義標簽:
IfTag.java,標簽處理器:
import javax.servlet.jsp.*; import javax.servlet.jsp.tagext.Tag; public class IfTag implements Tag { private boolean test; public void setTest(boolean test) { this.test = test; } public int doStartTag() throws JspException { if(this.test) return Tag.EVAL_BODY_INCLUDE; return Tag.SKIP_BODY; } public int doEndTag() throws JspException { // TODO Auto-generated method stub return 0; } public Tag getParent() { // TODO Auto-generated method stub return null; } public void release() { // TODO Auto-generated method stub } public void setPageContext(PageContext arg0) { // TODO Auto-generated method stub } public void setParent(Tag arg0) { // TODO Auto-generated method stub } } |
1. “private boolean test;”,這個成員名稱必須與JSP中自定義標簽的屬性名稱相同,JSP引擎會通過“setTest”方法,將屬性值傳遞過來。
2. “doStartTag()”當自定義標簽開始被執行時,JSP引擎會調此方法。在此方法中可以“Tag.EVAL_BODY_INCLUDE”告訴JSP引擎繼續向下執行標簽體中的內容,如果返回“Tag.SKIP_BODY”JSP引擎將不執行標簽體中的內容。
3. “doEndTag()”當標簽體被執行完成后,會調用些方法。
4. “getParent()”返回父標簽。
5. “release()”,當標簽處理器被銷毀前會調用此方法,可以在此方法中釋放。但這個處理器被JSP引擎實例化后,一般不會被釋放而是保存在內存中,留以后用。服務器被關閉時,會被釋放。
6. “setPageContext(PageContext arg0)”,JSP引擎將JSP頁面的運行環境,通過此方法傳遞給自定義標簽處理器。
7. “setParent(Tag arg0)”,如果標簽有父標簽時,JSP引擎會將父標簽對象傳遞進來。
MyEl.tld,自定義標簽描述文件。
<?xml version="1.0" encoding="UTF-8"?> <taglib version="2.0" 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"> <tlib-version>1.0</tlib-version> <short-name>SimpleTagLibrary</short-name> <uri>/SimpleTagLibrary</uri> <tag> <name>myIf</name> <tag-class>cn.itcast.cc.jsptag.IfTag</tag-class> <body-content>JSP</body-content> <attribute> <name>test</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag> </taglib> |
1. tld文件在工程中的存放位置,與web.xml存位置相同。
2. “<uri>”設置tld描述文件的URI,URI用于在JSP頁面中引入此tld文件。
3. “<tag>”定義一個自定義標簽。
4. “<name>”自定義標簽名。
5. “<tag-class>”自定義標簽處理器類,就是上邊編寫的類。
6. “<body-content>”自定義標簽的標簽體內容。(也可以設置為“empty”等)
7. “<attribute>”為自定義標簽添加一個屬性。
8. “<name>”自定義標簽屬性名。
9. “<required>”true為必須指定此屬性,false此屬性可為空。
10. “<rtexprvalue>”設置屬性為靜態的或是動態的。如果為false即靜態的,靜態的屬性值JSP引擎會將它自動轉換(必須是java的8種基本數據類型),比如<cc:MyIf test=”122”>JSP引擎會自動將“123”轉換為整數型,所以處理器的類成員可以定義為int型。如果為true即動態的,如果設置為將類型設置為Object可以接收任意數據類型的屬性。
Index.jsp,在JSP頁面中引入自定義標簽,并調用自定義標簽:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@taglib prefix="cc" uri="/SimpleTagLibrary" %> <html> <head> </head> <body> <cc:myIf test="${2>1}"> 自定義邏輯判斷標簽! </cc:myIf> </body> </html> |
2.編寫<cc:foEach var=”temp” items=”list”>…</cc:forEach>自定義標簽:
forEahTag.java,繼承自SimpleTagSupport,這個簡單簡化了Tag接口的操作。
import java.io.IOException; import java.util.*; import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.SimpleTagSupport; public class forEahTag extends SimpleTagSupport { // 記錄 items="xxx" 中的list對象 private List items; // 記錄 var="xxx"中的變量名稱 private String var; public void setItems(List items) { this.items = items; } public void setVar(String var) { this.var = var; } // SimpleTagSupport簡化了Tag接口的操作,在此只需要覆蓋doTag方法即可實現forEach。 @Override public void doTag() throws JspException, IOException { Iterator iter = this.items.iterator(); while (iter.hasNext()) { // 將單個對象設置到Context域中,在JSP頁面中可以使用${var}獲取對象。 this.getJspContext() .setAttribute(this.var, iter.next()); // 調用執行標簽體,需要一個輸出流參數, // 設置為null時JSP引擎自動將其替換為this.getJspContext().getOut() this.getJspBody().invoke(null); } } } |
“this.getJspBody()”返回JspFragment對象,JspFragment代表一個標簽體。JspFragment中不能包含JSP腳本元素。JspFragment.invoke就是調用標簽體。如果不調用此方法,就是忽略標簽體。
forEach也可以使用實現TagSupport接口實現,當標簽體被調用時,首先會調用“doStartTag”方法(只會調用一次),可以在這個方法里先輸出第一個成員。然后執行標簽體,最后調用“doAfterBody”方法,它是重點。在這個方法里再輸出下一個成員然后判斷是不是到了list尾,如果到了list結尾則返回IterationTag. SKIP_BODY;。如果沒有到list結尾則返回IterationTag. EVAL_BODY_AGAIN;,繼續重復執行標簽體,實現對list的遍歷!
MyEl.tld,添加如下內容:
<tag> <name>myForEach</name> <tag-class>cn.itcast.cc.jsptag.forEahTag</tag-class> <body-content>scriptless</body-content> <attribute> <name>var</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <name>items</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag> |
“<body-content>scriptless</body-content>”指定標簽體內為非腳本代碼。
Index.jsp添加如下內容:
<% List list = new ArrayList(); list.add("一"); list.add("二"); list.add("三"); this.getServletContext().setAttribute("list",list); %> <cc:myForEach items="${list}" var="temp"> ${temp } </cc:myForEach> |
在JSP頁面插入的代碼,是向List對象中插入成員,然后添加到ServletContext域中,${list}在ServletContext域中獲取list對象。
3.JSP自定義標簽高級部分:
修改標簽內容的標簽<cc:htmlFilter>與<cc:readFile>兩個標簽配合使用。
ReadFileTag.java
public class ReadFileTag extends SimpleTagSupport { // 記錄文件路徑 private String src; public void setSrc(String src) { this.src = src; } @Override public void doTag() throws JspException, IOException { // 獲取到文件的全路徑 PageContext pc = (PageContext) this.getJspContext(); ServletContext sc = pc.getServletContext(); String file = sc.getRealPath(src); // 使用字符緩沖流讀取文件 BufferedReader reader = new BufferedReader(new FileReader(file)); String line = null; while((line = reader.readLine()) != null){ // 寫到輸出流 this.getJspContext().getOut().write(line + "\r\n"); } } } |
注意代碼中的寫到輸出流,這個輸出流是父標簽傳遞進來的輸出流。
HtmlFilter.java
public class HtmlFilter extends SimpleTagSupport { @Override public void doTag() throws JspException, IOException { // 調用子標簽,并將輸出流傳遞進去。 CharArrayWriter caw = new CharArrayWriter(); this.getJspBody().invoke(caw); //這里之所以要寫義一個輸出流,是為了實現Html字符的轉換 this.getJspContext().getOut().write(filter(caw.toString())); System.out.println(caw.toString()); } // Html字符轉換 public static String filter(String message) { if (message == null) return (null); char content[] = new char[message.length()]; message.getChars(0, message.length(), content, 0); StringBuffer result = new StringBuffer(content.length + 50); for (int i = 0; i < content.length; i++) { switch (content[i]) { case '<': result.append("<"); break; case '>': result.append(">"); break; case '&': result.append("&"); break; case '"': result.append("""); break; default: result.append(content[i]); } } return (result.toString()); } } |
調用標簽體時,之所以傳遞一個自定義的輸出流。是因為,使用這個輸出流,獲取讀到的html文件的內容。然后將流中的html字符轉換后,再輸出到JSP頁面。
二、JSTL標簽
1.jstl標簽分為:
核心標簽庫
國際化標簽
數據庫標簽
XML標簽
JSTL函數
因為在JSP頁面中最好不要訪問數據庫和XML文件,這樣太不安全了而且程序邏輯混亂,所以就沒有講解這方面的使用。
2.jstl常用的標簽:
(1).<c:out>標簽:用于輸出一段文本內容到pageContext對象當前保存的“out”對象中。
(2). <c:set>標簽:用于設置各種Web域中的屬性,或者設置Web域中的java.util.Map類型的屬性對象或JavaBean類型的屬性對象的屬性。
(3). <c:remove>標簽:用于刪除各種Web域中的屬性。
(4). <c:catch>標簽:用于捕獲嵌套在標簽體中的內容拋出的異常。
(5). <c:if test=“”>標簽:可以構造簡單的“if-then”結構的條件表達式。
(6). <c:choose>標簽:用于指定多個條件選擇的組合邊界,它必須與<c:when>和<c:otherwise>標簽一起使用。
(7). <c:forEach>標簽:用于對一個集合對象中的元素進行循環迭代操作,或者按指定的次數重復迭代執行標簽體中的內容。
(8). <c:param>標簽:它可以嵌套在<c:import>、<c:url>或<c:redirect>標簽內,為這些標簽所使用的URL地址附加參數。此標簽自動進行URL編碼。
(9). <c:url>標簽:用于在JSP頁面中構造一個URL地址,其主要目的是實現URL重寫。還記得URL重寫是什么嗎?當session被禁用時,URL重寫就是為解決這一問題的!
(10). <c:redirect>標簽:用于將當前的訪問請求轉發或重定向到其他資源。
OK,上面是常用的標簽,使用方法去查手冊吧!東西太多了。老方今天特別提到一個標簽,是佟老師特別指出的。它在Structs中使用較多——C:check permission。
比如,一個管理頁面,有好多管理選項。但管理選項需要根據用戶的權限進行顯示,此時自定義一個檢查標簽,將User做為參數傳遞給標簽處理器。標簽處理器查看User的權限,然后確定是否執行標簽體。(標簽體中的內容為顯示相應的管理選項)
OK,明天就要老方就開始編寫一個將JavaSeb基礎部分的所有內容融會到一起的WEB應用用例。估計使用1天多點的時間。然后同學們有近兩天的個人實現時間。大家對此都十分期待!終于應用這段時間所學的知識,寫個像樣的東西出來。
加油!