标签的开发

Tag Library(标签库)

自定义标签实际是一个实现了特定接口的java类,封装了一些常用的功能,在运行时,标签将被相应的代码所替换,由web容器调用

API   javax.servlet.jsp.tagext中

要开发自定义标签,其核心就是编写标签处理器类,一个标签对应一个标签处理器类.所有的标签处理器类都需要实现JspTag接口(传统标签),简单标签实现SimpleTag接口

空标签<hello/>

带有属性的标签<max num1=”13” num2=“32”/>

带有内容的标签<greeting>welcome you!</greeting>

实现Tag接口的标签处理器的生命周期

setPageContext(PageContext pc)设置当前页面的上下文

setParment(Tag t)如果标签被嵌套,该方法被用来设置父标签

(1).容器在创建标签处理器的实例后,调用setPageContext()方法设置标签的页面上下文,然后调用setParent()方法设置这个标签的父标签,如果该标签没有父标签,就设置为null,页面初始化 

(2).设置标签的属性,如果没有属性则跳过这一步

(3).调用doStartTag()方法,该方法可以返回EVAL_BODY_INCLUDE或者SKIP_BODY

         ----EVAL_BODY_INCLUDE将标签体输出到当前的输出流中

         ----SKIP_BODY则忽略该标签体

(4).调用doEndTag()方法,该方法可以返回EVAL_PAGE或者SKIP_PAGE

         ----EVAL_PAGE执行JSP余下的部分

         ----SKIP_PAGE忽略JSP页面余下的部分

(5)容器会缓存标签处理器实例,一旦遇到同样的标签,则重复使用缓存的标签处理器实例

(6)当需要释放标签处理器实例时,调用release()方法

IterationTag接口

继承自Tag接口,新增方法doAfterBody,新增返回值EVAL_BODY_AGAIN,请求重复执行标签体

BodyTag接口

继承自IterationTag接口,新增了两个方法

setBodyConten(BodyContent b);//设置BodyContent属性,空标签不会调用该方法

doInitBody()//在调用上面的方法之后,标签体第一次被执行之前,该方法被调用,为标签体的执行做准备

 

标签库描述符(tld文件)

这是一个xml文档,用来描述一个标签库,包含标签的名字、标签处理器类、标签的属性等

开发自定义标签步骤

(1)编写一个实现Tag接口的java类(标签处理器)

(2)编写标签描述符(tld)文件,在tld文件对标签进行描述

 

 

在开发人员编写JSP页面时,还会引入一些逻辑

·控制jsp某一部分是否执行

doStart()方法的根据返回值的不同判断标签体内是否执行

return SKIP_BODY;//忽略标签体的部分

return EVAL_BODY_INCLUDE;//执行标签体的部分

·控制整个jsp页面是否执行

doEnd()方法的返回值判断jsp页面是否执行

return SKIP_PAGE;//不执行页面

return EVAL_PAGE;//执行下面的页面

·修改jsp页面是否重复执行

实现IterationTag接口(是Tag接口的子类,增加了一个doAfter())

标签体一执行完就返回一个值,用这个值来判断该标签体是否继续执行

if(条件)

    return IterationTag.EVAL_BODY_AGAIN;

else

    return IterationTag.SKIP_BODY;

·jsp页面的输出(实现BodyTag,增加了一个操作标签体的方法)

将标签体内的字符串改成大写

把标签体转成字符串,然后进行自定义的处理

String body=this.getBodyContent().getString();

       body.toUpperCase();

       try {

           this.pageContext.getOut().write(body);

       } catch (IOException e) {

           e.printStackTrace();

       }

       return EVAL_PAGE;

Tag接口的默认实现类TagSupport,覆盖它的doStart()方法,然后配tld文件,

 

简单标签接口()

Void setJspBody(JspFragment jspBody);

Void doTag();简单标签使用该方法完成所有的业务逻辑

·标签体是否执行

JspFragment jf=this.getJspBody();//代表标签体的JspFragment

       jf.invoke(this.getJspContext().getOut());

    不执行则不调用invoke方法

·标签体循环多次

JspFragment jf=getJspBody();//获取标签体

       JspWriter out=this.getJspContext().getOut();

       for(int i=0;i<10;i++){

           jf.invoke(out);

       }

·转换标签体的大小写

JspFragment jf=getJspBody();

       StringWriter sw=new StringWriter();

       jf.invoke(sw);//jf进入流中

       String content=sw.getBuffer().toString();

       content=content.toUpperCase();

       PageContext page=(PageContext) this.getJspContext();

       page.getOut().write(content);

·控制整个jsp页面是否执行

抛出异常就可以不执行下面的jsp页面

 

 

简单标签库的执行顺序

1.  执行标签时,先创建标签处理器对象

2.  调用setJspContext()方法,把页面的pageContext传递进去

3.  调用setParent()方法,把父标签传递进去

4.  调用setJspBody()方法,把代表标签体的jspFragment对象传递进去

5.  开始执行标签,调用doTag()方法,实现业务

 

 

开发带属性的标签

将标签体循环多少次?可以把次数封装成标签的属性

·在标签处理器中编写每个属性对应的setter方法

·在tld文件中描述标签的属性

private int count;

    public void setCount(int count) {

       this.count = count;

    }

public void doTag() throws JspException, IOException {

       for(int i=0;i<count;i++){

           this.getJspBody().invoke(null);

       }

    }

tld文件的配置

<tag>

        <name>demo4</name>

        <tag-class>javaTag.SimpleTag4</tag-class>

        <body-content>scriptless</body-content>

        <attribute>

           <name>count</name>

<!-- 根据反射获取count -->

           <required>true</required>

<!—指定该属性是否是必须的  -->

           <rtexprvalue>true</rtexprvalue>

<!-- 属性是否可以传递表达式 -->

        </attribute>

</tag>

<attribute>标签及其子元素,用来设置标签属性

<description>为该属性提供文本描述

<name>指定属性的名称

<required>指定该属性是否是必须的,默认值是false

<rtexprvalue>在运行时期,属性的值能否是一个表达式

<type>指定属性的类型

<fragment>指定属性是否是JspFragment对象,默认值为false

<dynamic-attributes>指定该标签是否支持动态属性

<example>用于提供一个使用该标签的信息描述

 

如果属性是8种基本数据类型,那么在JSP页面传递的是字符串,引擎会自动转换成为相应的类型

如果传入的不是基本类型数据,那么用${date},date被封装到了request域里面

简单标签的标签处理器实例不会被缓存,所以不能够重复使用,每当遇到标签时,容器就会创建一个信的标签处理器实例

Example

·防盗链标签

防止别的网站通过超链接指向自己的web资源

private String site;

    private String page;

    public void setSite(String site) {

       this.site = site;

    }

    public void setPage(String page) {

       this.page = page;

    }

 

 

PageContext pc=(PageContext) this.getJspContext();

       HttpServletRequest request=(HttpServletRequest) pc.getRequest();

       String refer=request.getHeader("referer");//获取是从那个页面跳转过来的

       HttpServletResponse response=(HttpServletResponse) pc.getResponse();//得到响应进行重定向

      

       if(refer==null || !refer.startsWith(site)){

           String root=request.getContextPath();//得到webroot的名称

           if(page.startsWith(root)){//如果page是以root开头

              response.sendRedirect(page);

           }else{

              response.sendRedirect(root+page);

           }

           throw new SkipPageException();//控制保护页面不要执行

       }

<cc:refe site="http://localhost:8080/" page="/index.jsp"/>

防止从site来的链接盗取web资源,必须经过本地首页访问才能查看

·if-else标签的开发

需要if通知else执行,那么如何获取if发过来的通知

解决方案:定义if,else的父标签,写三个标签处理器

在父标签处理器中维护一个boolean变量,当执行了一个子标签之后,改变boolean的值,就可以不执行另一个子标签

·for-each标签的开发

在标签处理器中维护一个Object对象(items)和String对象(var)

在doTag方法再通过instanceof判断Object属于哪种类型,进行相应的处理(collection、Object[]、Map)

还可以重构代码再维护一个collection引用,在set方法中将全部类型转成collection,对于数组类型的处理

if(items.getClass().isArray()){

              collection=new ArrayList();

              int len=Array.getLength(items);

               for(int i=0;i<len;i++){

                  Object obj=Array.get(items, i);

                  collection.add(obj);

              }

           }

·开发转义标签

public void doTag() throws JspException, IOException {

       StringWriter sw=new StringWriter();

       getJspBody().invoke(sw);//将标签体的内容交给sw

       String content=sw.getBuffer().toString();//拿到标签体的内容

       content=filter(content);//进行转义

       getJspContext().getOut().write(content);

    }

   

    public  String filter(String message) {

 

        if (message == null)

            return (null);

 

        char content[] = new char[message.length()];

        message.getChars(0, message.length(), content, 0);

        StringBuilder result = new StringBuilder(content.length + 50);

        for (int i = 0; i < content.length; i++) {

            switch (content[i]) {

            case '<':

                result.append("&lt;");

                break;

            case '>':

                result.append("&gt;");

                break;

            case '&':

                result.append("&amp;");

                break;

            case '"':

                result.append("&quot;");

                break;

            default:

                result.append(content[i]);

            }

        }

        return (result.toString());

    }

posted @ 2015-12-09 19:17  不再天真  阅读(335)  评论(0)    收藏  举报