self-confidence,the source of all the power

导航

jsp自定义标签

概念

自定义标签是用户定义的JSP语言元素。当JSP页面包含一个自定义标签时将被转化为servlet,标签转化为对被 称为tag handler的对象的操作,即当servlet执行时Web container调用那些操作。

JSP标签扩展可以让你创建新的标签并且可以直接插入到一个JSP页面。 JSP 2.0规范中引入Simple Tag Handlers来编写这些自定义标记。

你可以继承SimpleTagSupport类并重写的doTag()方法来开发一个最简单的自定义标签。

SimpleTagSupport类的API

见文档 http://tomcat.apache.org/tomcat-5.5-doc/jspapi/

 public class SimpleTagSupportextends java.lang.Objectimplements SimpleTag

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.

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服务器在处理自定义标签的时候,方法调用顺序如下:

           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文件中的配置

1、控制标签体是否执行或显示,如果不显示,则在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>

2、修改标签体

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);//将修改后的标签体输出 }

3、控制标签余下的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();
}

3、带属性的标签

<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>

4、防盗链标签

原理:假如有主页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();
         }
}

 5、带父标签的标签

举一个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);
               }
         }
}

 6、 遍历列表标签 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} } } }

 

 

 

posted on 2017-07-16 00:32  漩涡鸣人  阅读(241)  评论(0编辑  收藏  举报