jsp自定義標籤

概念html

自定義標籤是用戶定義的JSP語言元素。當JSP頁面包含一個自定義標籤時將被轉化爲servlet,標籤轉化爲對被 稱爲tag handler的對象的操做,即當servlet執行時Web container調用那些操做。java

JSP標籤擴展可讓你建立新的標籤而且能夠直接插入到一個JSP頁面。 JSP 2.0規範中引入Simple Tag Handlers來編寫這些自定義標記。web

你能夠繼承SimpleTagSupport類並重寫的doTag()方法來開發一個最簡單的自定義標籤。apache

SimpleTagSupport類的APIapi

見文檔 http://tomcat.apache.org/tomcat-5.5-doc/jspapi/ 數組

 public class SimpleTagSupportextends java.lang.Objectimplements SimpleTagtomcat

A base class for defining tag handlers implementing SimpleTag.服務器

The SimpleTagSupport class is a utility class intended to be used as the base class for new simple tag handlers. The SimpleTagSupport class implements the SimpleTag interface and adds additional convenience methods including getter methods for the properties in SimpleTag.less

Since: 2.0
Method Summary
 void doTag()
          Default processing of the tag does nothing.
static JspTag findAncestorWithClass(JspTag from, java.lang.Class klass)
          Find the instance of a given class type that is closest to a given instance.
protected  JspFragment getJspBody()
          Returns the body passed in by the container via setJspBody.
protected  JspContext getJspContext()
          Returns the page context passed in by the container via setJspContext.
 JspTag getParent()
          Returns the parent of this tag, for collaboration purposes.
 void setJspBody(JspFragment jspBody)
          Stores the provided JspFragment.
 void setJspContext(JspContext pc)
          Stores the provided JSP context in the private jspContext field.
 void setParent(JspTag parent)
          Sets the parent of this tag, for collaboration purposes.

從其方法中能夠看出,只要繼承了SimpleTagSupport就能夠獲得pageContext(getJspContext),標籤體(getJspBody)。而且web服務器在處理自定義標籤的時候,方法調用順序以下:jsp

           Web服務器----->jsp------>實例化標籤處理器
           ------>調用setJspContext把pageContext傳遞給標籤處理器
           ------>調用setParent把父標籤傳遞進去,沒有則傳null
           ------>調用setJspBody傳遞封裝標籤體的JspFragment
           ------>執行自定義標籤,調用doTag
           ------>系統自動處理銷燬標籤處理器,結束調用

從上面調用順序看,咱們只須要重寫doTag方法實現咱們的業務處理就能夠了。

自定義標籤的步驟

在開發中,只要按如下步驟一步步來,就不會出錯。

第一步 建立標籤,格式以下

//不帶標籤體的標籤
<ex:tagname>
</ex:tagname>
--------------------
<ex:tagname/>
--------------------
//帶標籤體的
<ex:tagname>
      helloworld!
</ex:tagname>
--------------------
//帶屬性的
<ex:tagname count="3">
輸出3次標籤體
</ex:tagname>

第二步,建立標籤實體類,即在web應用下的src目錄下建立一個繼承SimpleTagSupport類的標籤類,在doTag方法裏進行邏輯處理

public class HelloTag extends SimpleTagSupport {

  public void doTag() throws JspException, IOException {
    JspWriter out = getJspContext().getOut();//取到jsp中的printWriter
    out.println("Hello Custom Tag!");
  }
}

第三步,在tld文件中對標籤進行聲明,並將文件命名爲ex.tld並放在WEB-INFO目錄下,在jsp文件中進行引用時,最好將prefix寫成與tld文件名同樣,便於查找

<taglib>
  <tlib-version>1.0</tlib-version>
  <jsp-version>2.0</jsp-version>
  <short-name>Example TLD</short-name>
<uri>http://www.extag.cn</uri> //導入標籤的時候需填寫這個uri
<tag> <name>Hello</name> <tag-class>ex.package.HelloTag</tag-class> //標籤處理類的全名 <body-content>empty</body-content> //表示標籤體爲空 </tag> </taglib>

第四步,在jsp文件中導入並使用

<%@ taglib prefix="ex" uri="http://www.extag.cn"%>
<html>
  <head>
    <title>A sample custom tag</title>
  </head>
  <body>
    <ex:Hello/>
  </body>
</html>

幾個常見例子

在例子中只列出doTag中的關鍵代碼 及tld文件中的配置

一、控制標籤體是否執行或顯示,若是不顯示,則在doTag中空實現

public void doTag () throws JspException,IOException{
         JspFragment jf = this.getJspBody();
         jf.invoke(this.getJspContext().getOut());
}

 

<tag>
         <name>ex1</name>
         <tag-class>package.ex1</tag-class>
         <body-content>scriptless</body-content>  
          //2.0版本sun公司新規範,不容許jsp中寫java代碼, 以前的寫法:<body-content>JSP</body-content>
</tag>

二、修改標籤體

public void doTag () throws JspException,IOException{
         JspFragment jf = this.getJspBody();
StringWriter sw = new StringWriter();
jf.invoke(sw);
String content = sw.toString;
//todo something for modify content
this.getJspContext().getOut().write(content);//將修改後的標籤體輸出 }

三、控制標籤餘下的jsp不執行

這裏列出doTag方法的說明

public void doTag()
           throws JspException,
                  java.io.IOException
Throws:
JspException - Subclasses can throw JspException to indicate an error occurred while processing this tag.
SkipPageException - If the page that (either directly or indirectly) invoked this tag is to cease evaluation. A Simple Tag Handler generated from a tag file must throw this exception if an invoked Classic Tag Handler returned SKIP_PAGE or if an invoked Simple Tag Handler threw SkipPageException or if an invoked Jsp Fragment threw a SkipPageException.
java.io.IOException - Subclasses can throw IOException if there was an error writing to the output stream
要想不執行標籤後的jsp內容,只須要拋出SkipPageException便可。
public void doTag () throws JspException,IOException{
       throw new SkipPageException();
}

三、帶屬性的標籤

<ex:exam3 count="3">
     body
</ex:exam3>

與以前幾種不一樣的是,須要在標籤處理類中定義接收屬性值的變量,並實現setter方法,setter方法在接收 屬性時支持8種基本類型的自動轉換, 像date類的 String d="1983-11-30", 如在標籤處理類中用Date變量接收,因爲不支持非基本類型的轉換,在執行標籤時會報錯。

這個例子要實現的功能是重複輸出標籤體,代碼以下

public class Exam3 extends SimpleTagSupport{
        private int count;
        public void setCount(int count){
                this.count = count;
        }

        public void doTag () throws JspException,IOException{
               JspFragment jf = this.getJspBody();
               for (int i=0; i<count; i++){
                      jf.invoke(null);// null時默認輸出標籤體
               }
         }
}
<tag>
         <name>exam3</name>
         <tag-class>package.ex3</tag-class>
         <body-content>scriptless</body-content>  
         <attribute> 
                 <name>count</name>
                 <required>true</required>  //表示 爲必填屬性
                 //爲true時,表示該屬性同時支持字串賦值和el表達式賦值,count="${user.count}"
                 <rtexprvalue>true</rtexprvalue>
         </attribute>
</tag>

四、防盜鏈標籤

原理:假若有主頁A, 目標頁面B, A上面有連接可點擊來到頁面B,這時你的朋友以爲頁面B頗有意思,因而把頁面B經過QQ或其餘方式分享給你,你點開頁面後,奇怪的是發現並非頁面B,而是來到了充滿了廣告的主頁面A,這就是防盜鏈技術。

標籤訂義

<ex:exam4 site="http://localhost:8080" page="/index.html">
</ex:exam4>

doTag方法

public class Exam4 extends SimpleTagSupport{
        private String site;
        private String page;
        public void setSite(String s){
                this.site= s;
        }
        public void setPage(String s){
                this.page= s;
        }


        public void doTag () throws JspException,IOException{
               PageContext pc = (PageContext)this.getJspContext();
               HttpServletRequest request = (HttpServletRequest)pc.getRequest();
               HttpServletResponse = response = (HttpServletResponse)pc.getResponse();
               
               String refer = request.getHeader("referer");
               if (refer == null || !refer.startWith(site)){
               //表示不是從主頁面A過來的,跳轉到主頁面
                       response.sendRedirect(request.getContextPath()+page);
               }
               //拋異常,不執行標籤體後面的jsp內容,從而達到了防盜效果
               throw new SkipPageException();
         }
}

 五、帶父標籤的標籤

舉一個if-else的例子,首先定義標籤

<ex:choose>
     <ex:when istrue = "true">
           //爲true時執行
     </ex:when>
     <ex:otherwise>
            //上一個條件爲false執行
     </ex:otherwise>
</ex:choose>

實現原理:由父標籤choose記錄整個標籤是否有子標籤已經執行,在依次執行子標籤時,若是條件知足,則判斷父標籤是否已標誌已有子標籤執行過了,若是沒有,則執行並將父標籤標記好;若是有,則不執行。

doTag方法

public class ChooseTag extends SimpleTagSupport{
        private boolean istrue;
        public void setIstrue(boolean s){
                this.istrue= s;
        }
        public boolean isIstrue(boolean s){
                this.istrue= s;
        }

        public void doTag () throws JspException,IOException{
                this.getJspBody().invoke(null);
         }
}

再看他的子tag

public class WhenTag extends SimpleTagSupport{
        private boolean istrue;
        public void setIstrue(boolean s){
                this.istrue= s;
        }
        public boolean isIstrue(boolean s){
                this.istrue= s;
        }

        public void doTag () throws JspException,IOException{
                //拿到父tag
               ChooseTag parent = (ChooseTag)this.getParent();
               if(istrue && !parent.isIstrue()){
                     this.getJspBody().invoke(null);
                     //調用後,告訴父標籤已經執行了
                     parent.setIsistrue(true);
               }
         }
}

otherwise Tag,全部條件都不知足的狀況下執行

public class OtherwiseTag extends SimpleTagSupport{

        public void doTag () throws JspException,IOException{
                //拿到父tag
               ChooseTag parent = (ChooseTag)this.getParent();
               if( !parent.isIstrue()){
                     this.getJspBody().invoke(null);
                     //調用後,告訴父標籤已經執行了
                     parent.setIsistrue(true);
               }
         }
}

 六、 遍歷列表標籤 foreach

 這個標籤實現比較有技巧,雖然標準庫裏有,也在此記錄一下,它的實現技巧值得借籤,先看定義

<ex:foreach  var="name" items="$(list)">
      ${name}
</ex:foreach>

doTag方法

public class Exam4 extends SimpleTagSupport{
        private Object items;
        private String var;
        public void setItems(String s){
                this.items= s;
        }
        public void setVar(String s){
                this.var= s;
        }

        public void doTag () throws JspException,IOException{
               //直接強轉成列表 
               List list = (List) items;
               Iterator it = list.iterator();
               while (it.hasNext()){
                     Object val = it.next();
                     //不知道具體類型的狀況下,將他直接存到內存裏再通知jsp去取出來;
                     this.getJspContext().setAttribute(var,val);
                     this.getJspBody().invoke(null);  //去執行標籤體,而它恰好是${var}
               }
         }
}

很顯然這個foreach Tag不能處理Map, array的集合類型,還能夠再優化一下

public class Exam4 extends SimpleTagSupport{
        private Object items;
        private String var;
        private Collection  c;
        public void setItems(Object s){
                this.items= s;
                //map 屬於雙列集合,在賦值的時候,將map,array轉化爲單列集合,其餘地方徹底不用改代碼
               if (items instanceof Collection){
                        collections = (Collection)items;
                }
                if (items instanceof Map){
                        Map map = itmes;
                        collections = map.entrySet(); //在這裏能夠聯想到使用時是這樣用的,${entry.key}, ${entry.value}
                }
                if (items instanceof Object[]){
                        Objects obj = (Object[])items;
                        collections = Arrays.asList(obj);
                }
//支持八種基本類型的數組
//利用Array的反射功能,將全部的數組轉化成list
if(items.getClass().isArray()
){
this.collections = new ArrayList();
int length = Array.getLength(items);
for (int i=0; i <length; i++){
Object obj = Array.get(items, i);
collections.add(obj);
}
} }
public void setVar(String s){ this.var= s; } public void doTag () throws JspException,IOException{ Iterator it = collections.iterator(); while (it.hasNext()){ Object val = it.next(); //不知道具體類型的狀況下,將他直接存到內存裏再通知jsp去取出來; this.getJspContext().setAttribute(var,val); this.getJspBody().invoke(null); //去執行標籤體,而它恰好是${var} } } }
相關文章
相關標籤/搜索