zoukankan      html  css  js  c++  java
  • JavaWeb之 JSP:自定义标签

    jsp的内置标签和jstl标签库内的标签都满足不了需求,这时候就需要开发者自定义标签。

     

    自定义标签

    下面我们先来开发一个自定义标签,然后再说它的原理吧!

     

    自定义标签的开发步骤

     

    步骤一

    编写一个普通的java类,继承TagSupport~

     

    package com.vmaxtam.dotest;
    import javax.servlet.jsp.tagext.TagSupport;
    
    public class MyTagTest extends TagSupport {
        
    }

     

    步骤二

    重写父类的setPageContext方法,用于得到当前jsp页面的pageContext对象。

     

    public class MyTagTest extends TagSupport {    
        private PageContext pageContext;
        @Override
        public void setPageContext(PageContext pageContext) {
            this.pageContext=pageContext;
        }
    }

     

    步骤三

    重写父类的doStartTag方法,里面写上你定义的标签的java操作,这里我定义的标签用作向浏览器输出一大段信息:

     

    @Override
        public int doStartTag() throws JspException {
            
        try {
          pageContext.getResponse().getWriter().write("这是我写的一大段信息:ABCDEFGHIJKLMNOPQRSTUVWXYZ");
            } catch (IOException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
            return super.doStartTag();
        }

     

    这样就完成一个标签处理程序了~别着急,写完程序我们还需要注册它。

     

    步骤四

    在你的web应用目录下,找到WEB-INF文件夹,在里面新建一个tld类型的文件

    然后再里面注册你的标签吧:

    <?xml version="1.0" encoding="ISO-8859-1" ?>
    <!DOCTYPE taglib
      PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
      "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
    <taglib>
      <tlib-version>1.0</tlib-version><!-- 代表标签库的版本号 -->
      <jsp-version>1.2</jsp-version><!-- 代表jsp的版本 -->
      <short-name>mtt</short-name><!-- 你的标签库的简称 -->
      <uri>http://vmaxtam.com/mytag</uri><!-- 你标签库的引用uri -->
      
      <tag>
          <name>mytah</name><!-- 你定义的标签的名称 -->
           <tag-class>com.vmaxtam.dotest.MyTagTest</tag-class><!-- 对应的标签处理程序:包名+类名 -->
          <body-content>JSP</body-content><!-- 标签体内容的格式 -->
      </tag>
     </taglib>

    如果你忘记了怎么写,可以参考jstl里的tld文件~

     

    步骤五

    你要在使用你定义的标签的jsp页面导入你的标签库!就像导入类包一样

    只需在jsp页面写上下面内容:

     

    <%@taglib uri="http://vmaxtam.com/mytag" prefix="mmt" %>

     

    步骤6

    以上5步已经把准备工作都做好了~下面我们来使用标签吧!

     

    <html>
      <head>   
        <title>My JSP 'testit.jsp' starting page</title>
      </head>
      
      <body>
          <mmt:mytag></mmt:mytag>
      </body>
    </html>

     

    浏览器效果如下:

    这样,我们就完成了一次自定义标签了,虽然我们知道步骤,但是不知道为什么这样就行,所以,下面来说一下它的原理:

     

    自定义标签的原理

    1)当服务器打开时,就会加载WEB-INF下的资源文件,包括web.xml 和 tld文件,把它们加载到内存

    2)我们在浏览器输入http://localhost:8080/TestArea/testit.jsp来访问jsp页面

    3)服务器读取testit.jsp里的内容,当读到

    <%@taglib uri="http://vmaxtam.com/mytag" prefix="mmt" %> 

    这一句的时候,就会在内存中找是否存在urihttp://vmaxtam.com/mytagtld文件,找不到就会报错

    4)继续读取jsp页面,读到<mmt:mytag>这个标签的时候,就会通过uri找到tld文件,在tld文件中找到mytab是否被定义,是的话就得到它的tag-class的内容,然后去找到它对应的标签处理程序

    5)实例化标签处理程序,利用生成的对象调用它里面的方法

    这里服务器对标签处理程序里的方法也有一定的调用顺序      A)void setPageContext(PageContext pc)  --传入pageContext对象

    B)void setParent(Tag t)              --如果有父标签,传入父标签对象,如果没有,则传入null

    C)int doStartTag()                 --开始执行标签时调用。

    D)int doEndTag()                --结束标签时调用

    E)void release()                  --释放资源

     

    如果你没有重写上面的方法,系统将会调用它的父类里的方法~

    为什么会是这个顺序调用,是有证据的,下面我们来看看jsp被翻译为java源文件里的截取:

     

    private boolean _jspx_meth_itcast_005fshowIp_005f0(PageContext _jspx_page_context)
              throws Throwable {
        PageContext pageContext = _jspx_page_context;
        JspWriter out = _jspx_page_context.getOut();
        //  itcast:showIp
       1) 实例化ShowIpTag对象
        gz.itcast.tag.ShowIpTag _jspx_th_itcast_005fshowIp_005f0 = (gz.itcast.tag.ShowIpTag) _005fjspx_005ftagPool_005fitcast_005fshowIp_005fnobody.get(gz.itcast.tag.ShowIpTag.class);
        2)调用setPageContext方法
     _jspx_th_itcast_005fshowIp_005f0.setPageContext(_jspx_page_context);
        3)调用setParent方法
        _jspx_th_itcast_005fshowIp_005f0.setParent(null);
      4)调用doStartTag方法
        int _jspx_eval_itcast_005fshowIp_005f0 = _jspx_th_itcast_005fshowIp_005f0.doStartTag();
       5)调用doEndTag方法
        if (_jspx_th_itcast_005fshowIp_005f0.doEndTag() == javax.servlet.jsp.tagext.Tag.SKIP_PAGE) {
          _005fjspx_005ftagPool_005fitcast_005fshowIp_005fnobody.reuse(_jspx_th_itcast_005fshowIp_005f0);
          return true;
        }
        _005fjspx_005ftagPool_005fitcast_005fshowIp_005fnobody.reuse(_jspx_th_itcast_005fshowIp_005f0);
        return false;
      }

     

    控制标签体内容 与 结束标签后的内容

    自定义标签可以可控制标签体内的文本 和 结束标签后的文本是否输出~

     

        @Override//遇到开始标签时执行的方法
        public int doStartTag() throws JspException {
            
            //return Tag.SKIP_BODY; //标签体内容不向浏览器输出
            return Tag.EVAL_BODY_INCLUDE;//标签体内容向浏览器输出
        }    
        @Override//遇到结束标签后执行的方法
        public int doEndTag() throws JspException {
        
            //return Tag.EVAL_PAGE;//结束标签后的内容输出到浏览器
            return Tag.SKIP_PAGE;//结束标签后的内容不输出到浏览器        
        }

     

    那么如何重复输出标签体内的文本内容呢?TagSupper还提供了一个doAftetBody方法,我们只需要这样做:

     

        int i = 4;
        @Override//每输出一次标签体的内容都会调用一次这个方法
        public int doAfterBody() throws JspException {
            while(true)
            {
                if(i>0)
                {
                    i--;
                    return IterationTag.EVAL_BODY_AGAIN;//再执行一次便签体内的内容
                }else{
                    break;
                }        
            }
            return Tag.SKIP_BODY;//不输出标签体的内容
        }

     

    以上的内容都是控制标签体的内容输出的问题,那么能不能改变标签体力的内容呢?

    很可惜,用TagSupport是不行,但是我们可以用它的子类BodyTagSupport,那么久写一个类继承BodyTagSupport类吧~

     

    public class MyTagTest extends BodyTagSupport {
    
        private PageContext pageContext;
    
        @Override
        public void setPageContext(PageContext pageContext) {
            this.pageContext = pageContext;
        }
        
        @Override
        public int doStartTag() throws JspException {
            
            //返回BodyTag.EVAL_BODY_BUFFERED,表示输出标签体内容
            //返回Tag.SKIP_BODY,表示不输出内容
            return BodyTag.EVAL_BODY_BUFFERED;
            //return Tag.SKIP_BODY;
        }
        
        @Override
        public int doEndTag() throws JspException {
            
            //得到BodyContent对象,它包装了标签体里的内容
            BodyContent bodyContent = this.getBodyContent();
            
            //利用getString方法得到字符串
            String content = bodyContent.getString();
            
            //改变字符串内容,将小写改为大写
            String change = content.toUpperCase();
            
            //输出到浏览器
            try {
                this.pageContext.getResponse().getWriter().write(change);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return Tag.SKIP_PAGE;
        }
    }

     

    以上~就是自定义标签的创建步骤会原理,还有一些标签体内容的处理方法,大家觉得容易吗?

    对,十分的不容易啊,用这种方法定义的标签我们称为传统标签,所以这是一个社会问题,是时候就会有人站出来,写出一组代码来解决这个问题了,这组代码称为:简单标签

     

    简单标签

    为什么要学习传统标签

    学习传统标签是为了以后维护到一些旧系统!!

    简单标签比传统标签简单在标签处理器类的编写简单了!!!

     

    简单便签的开发步骤

    同样的,我们先学习简单标签的开发步骤,然后再说说它的原理

     

    步骤一

    编写标签处理器类,也就是一个普通的类,继承SimpleTagSupport类。然后重写它的doTag()方法:

     

    public class MySimpleTag extends SimpleTagSupport {
        
            @Override//当遇到标签时就会执行这个方法
            public void doTag() throws JspException, IOException {
                System.out.println("执行了简单标签里的doTag()方法~");
            }
    }

     

    步骤二

    tld文件内注册这个标签吧~这个过程和传统标签一样

     

       <tag>
          <name>simpletag</name>
          <tag-class>com.vmaxtam.dotest.MySimpleTag</tag-class>
          <body-content>scriptless</body-content><!--这里要用这个处理-->
      </tag>

     

    步骤三

    JSP文件中导入标签库(这个过程和传统标签一样)

    步骤四

    使用该标签(这个过程和传统标签一样)

     

    以上就是简单标签的定义过程了,和传统标签相比,他简单就简单在不用重写很多方法。

     

    简单标签的原理

    一)和传统标签一样,得到tag-class字符串,找到标签处理程序类

    二)实例化标签处理程序类

    三)利用对象调用方法。。。。

    和传统标签相比,简单标签调用的方法不相同:

    SimpleTag接口的方法执行过程:

     1) void setJspContext(JspContext pc)  --设置pageContext对象,传入pageContext对象。JspContextPageContext的父类。在标签处理器类中通过this.getJspContext()方法得到PageContext对象。

     

     2void setParent(JspTag parent)        --传入父标签对象,如果没有父标签,则不调用次方法。通过getParent方法得到父标签对象

     

     3void setJspBody(JspFragment jspBody)   --传入标签体内容。标签体内容封装到JspFragment方法中。通过getJspBody方法得到标签体内容。如果没签体,不调用次方法。

     

     4void doTag()                       --开始标签和结束标签都执行次方法。

     

    为什么是这样调用方法呢,也是有证据的:

    private boolean _jspx_meth_itcast_005fsimpleDemo_005f0(PageContext _jspx_page_context)
              throws Throwable {
        PageContext pageContext = _jspx_page_context;
        JspWriter out = _jspx_page_context.getOut();
        //  itcast:simpleDemo
        1)实例化SimpleDemo对象
        gz.itcast.b_simple.SimpleDemo _jspx_th_itcast_005fsimpleDemo_005f0 = new gz.itcast.b_simple.SimpleDemo();
        org.apache.jasper.runtime.AnnotationHelper.postConstruct(_jsp_annotationprocessor, _jspx_th_itcast_005fsimpleDemo_005f0);
        2)调用setJspContext方法,传入PageContext对象
        _jspx_th_itcast_005fsimpleDemo_005f0.setJspContext(_jspx_page_context);
        3)调用setParent方法,如果没有父标签,不执行。
        4)调用setJspBody方法,传入标签体内容
        _jspx_th_itcast_005fsimpleDemo_005f0.setJspBody(new Helper( 0, _jspx_page_context, _jspx_th_itcast_005fsimpleDemo_005f0, null));
        5)调用doTag方法,执行标签
        _jspx_th_itcast_005fsimpleDemo_005f0.doTag();
        org.apache.jasper.runtime.AnnotationHelper.preDestroy(_jsp_annotationprocessor, _jspx_th_itcast_005fsimpleDemo_005f0);
        return false;
      }

    控制标签体文本 与 结束标签后内容 是否输出

    我们可以通过JspFragment对象来控制的~

    标签体内容:

    要输出: 在doTag()方法中执行jspFrament.invoke()方法

    不输出: 什么都不做!!

    结束标签后的内容:

    要输出:什么都不做!

    不输出:在doTag()方法中抛出一个SkipPageException异常~

     

    @Override
            public void doTag() throws JspException, IOException {
                JspFragment jspBody = this.getJspBody();
                jspBody.invoke(null);
                
                throw new SkipPageException();
            }

     

    那么如何循环输出标签体内容呢,在简单标签中实现十分简单,在doTag方法中写上

     

     

        for(int i=1;i<=5;i++){
            jspBody.invoke(null);//默认写出都浏览器
        }

    改变标签体里的内容

    doTag方法中写上:

    //4.1 创建一个临时的Writer输出流(容器)
            StringWriter writer = new StringWriter();
            
            //4.2 把标签体内容拷贝到临时的Writer流中
                 JspFragment jspBody = this.getJspBody();
    
            jspBody.invoke(writer);
            
            //4.3 从临时的Writer流中取出标签体内容
            String content = writer.toString();
            
            //4.4 改变标签体内容
            content = content.toUpperCase();
            
            //4.5 把改变后的内容写出到浏览器中
            //jspBody.invoke(null);如果这样写,那么还是输出原来的内容
            this.getJspContext().getOut().write(content);

    标签体内容的输出格式

    除了能设置标签体内容是否输出,还能够设置它的输出格式,那么它有什么样的输出格式呢?

    可以有以下输出格式:

    JSP: 表示输出的标签体内容可以包含jsp脚本,且可以执行此脚本。此值只能用在传统标签中。

    scriptless: 表示输出的标签体内容不能包含jsp脚本,如果包含则报错。  

    empty:表示没有标签体内容。即是空标签。如果不是空标签,则报错。

    tagdependent: 表示输出的标签体内容可以包含jsp脚本。但不执行jsp脚本(直接原样输出)

     

    那么我们要在tld文件内设置文本的输出格式:

     

      <tag>
            <name>tagDemo</name>
            <tag-class>gz.itcast.a_tag.TagDemo1</tag-class>
               <body-content>JSP</body-content><!--在这里设置-->
      </tag>

    上面都是在讨论标签体内容的输出,标签里还可以设置属性的,那么自定义标签如何定义标签的属性呢?

     

    自定义标签的属性

    这个过程我们在简单标签内实现,以下是操作步骤

     

    步骤一

    在标签处理器类内声明一个成员变量,,这个成员变量就用来接受标签属性的值,然后再标签处理器类内为这个成员变量生成一个setter方法:

     

    public class MySimpleTag extends SimpleTagSupport {
        
        private Integer num;    
        
        public void setNum(Integer num) {
            this.num = num;
        }

    步骤二

    要到tld文件注册这个属性,属性药注册在响应标签的<Tag>标签内

     

      <tag>
          <name>simpletag</name>
          <tag-class>com.vmaxtam.dotest.MySimpleTag</tag-class>
          <body-content>scriptless</body-content>
          
          <attribute>
              <name>num</name> <!-- ??? -->
              <required>true</required><!-- ???????????????? -->
              <rtexprvalue>true</rtexprvalue><!-- ???????EL??? -->
          </attribute>
      </tag>

    步骤三

    这样就可以去使用属性了~

     

    <body>
          <mmt:simpletag num="1001">我是标签里的内容</mmt:simpletag>我是标签后的内容
      </body>

     

    上面的内容就可以创建一个基本功能的自定义标签了~

     

     

     

     

     

  • 相关阅读:
    java线程学习之volatile关键字
    java线程学习之yield方法
    java线程学习之join方法
    小程序hideTarBar隐藏TabBar后,获取windowHeight不准确问题
    canvas等base64格式上传到服务端直传到oss
    服务器关于node的注意事项
    node.js连接本地数据库
    小程序(mpvue框架)的总结
    git代码的注意
    js里的实用小技巧
  • 原文地址:https://www.cnblogs.com/vmax-tam/p/4145334.html
Copyright © 2011-2022 走看看