• (十三)自定义JSTL标签


    前面的博客,我们讲过了 自定义 el函数

    讲一个 自定义标签技术 ;


    目录


    自定义标签

    自定义标签 主要用于移除JSP页面中的java代码 ;

    要使用自定义标签移除JSP页面中的java代码,只需要完成以下两个步骤:

    1. 编写一个实现Tag接口的Java类,把页面java代码移到这个java类中(标签处理器类
    2. 编写标签库描述符(tld)文件,在tld文件中把标签处理器描述成一个标签 ;(tld文件,一般放在WEB-INF下面

    快速入门:使用标签输出客户机IP

    分析自定义标签的执行流程

    Tag接口的方法都是由服务器调用的,但是在调用dostartTag的时候,会先调用接口的setPageContext()方法,因此,我们可以得到pageContext对象;

    自定义标签运行原理

    当浏览器,访问web服务器的jsp页面时,遇到自定义标签;

    1. 首先实例化出一个自定义标签类的对象
    2. 利用Tag接口中的setPageContext方法,将页面的pageContext对象传递给处理器类的对象;
    3. 再调用setParent方法,如果标签有父标签,则将父标签对象作为参数,传递进去;如果没有父标签,则参数为null
    4. 上述工作完成以后,自定义标签类对象,相当于完成初始化操作了,这时候服务器开始执行标签,遇到开始标签,就执行doStartTag方法 ;
    5. 如果标签有标签体,服务器一般会执行标签体 ;
    6. 执行到标签结束符,就会调用doEndTag方法;
    7. 整个标签执行完毕以后,服务器一般会调用release方法,释放标签工作时锁占用的资源 ;

    工作原理的源码
    机器灵 砍菜刀

    从源码,我们可以看出,release方法并没有得到执行,服务器把自定义标签对象,放到了缓存中,便于下次访问;

    就像servlet对象一样,一直驻留在服务器内存中,直到服务器关闭 ;

    其中tld文件,服务器会把WEB-INF下面的tld文件,全部加载到内存中,保存到一个Map集合中,其中键就是jsp文件中的uri

    案列:使用标签输出客户机IP

    我们新建一个java类,让它继承 TagSupport 类;重写我们需要的方法 ,这里我们仅仅需要重写 doStartTag 方法;

     public class ViewTag extends TagSupport {
    
    //    @Override
        public int doStartTag() throws JspException {
    //        利用pageContext对象,获取request
            HttpServletRequest request = (HttpServletRequest) this.pageContext.getRequest();
            String ip = request.getRemoteAddr() ;
            JspWriter out = this.pageContext.getOut() ;
            try {
                out.write(ip);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
    
            return super.doStartTag();
        }
    }
    

    然后我们需要写一个 TLD 文件 ;

    如何编写TLD文件?!

    笔者也不会写,但是笔者会抄啊! (具体怎么抄,看笔者博客 怎么抄tld文件(可以点击)

    下面是我打开的 c.tld 的部分代码

    我们怎么写 TLD 文件,就这些代码,稍微改吧改吧,就是我们自己的 TLD 文件了

    <?xml version="1.0" encoding="UTF-8" ?>
    
    <taglib 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"
        version="2.0">
    
      <description>JSTL 1.1 core library</description>
      <display-name>JSTL core</display-name>
      <tlib-version>1.1</tlib-version>
      <!-- 这里我们要改下,改成我们自己的简化名,比如笔者,改成了自己的名字缩写 -->
      <!--  <short-name>c</short-name> -->
      <short-name>yaz</short-name>
       <!-- 这里我们也要改下,改成我们自己的uri,比如笔者,改成了自己的网址,随便写一个也行 -->
      <!--  <uri>http://ijava.xin./jsp/jstl/core</uri> -->
      <uri>http://ijava.xin</uri>
    
    <!--  这是一大堆描述信息,直接删掉就好了
      <validator>
        <description>
            Provides core validation features for JSTL tags.
        </description>
        <validator-class>
            org.apache.taglibs.standard.tlv.JstlCoreTLV
        </validator-class>
      </validator>
    -->
    
    <!-- 下面是一个例子,如何描述EL函数  -->
      <tag>
      <!-- 描述这个标签是干嘛的;我们直接删掉
        <description>
            Catches any Throwable that occurs in its body and optionally
            exposes it.
        </description>
        -->
        <!-- 标签名字,如果你定义的标签有属性,就写;没属性就不需要写下面的那些 -->
        <name>catch</name>
         <!-- 标签方法来源的类,要写完整类名,留着 -->
        <tag-class>org.apache.taglibs.standard.tag.common.core.CatchTag</tag-class>
         <!-- 是否有标签体,没有的话就写empty,有的话,就写上标签体是啥,留着 -->
        <body-content>JSP</body-content>
         <!-- 标签属性,留着 -->
        <attribute>
         <!-- 还是描述信息,删掉删掉
            <description>
    Name of the exported scoped variable for the
    exception thrown from a nested action. The type of the
    scoped variable is the type of the exception thrown.
            </description>
           -->
            <!-- 属性名字,留着 -->
            <name>var</name>
             <!-- 是否是必备属性,必备属性就写true,写了true的属性,就必须写上值了,留着 -->
            <required>false</required>
             <!-- 全称是 run time expression alue 翻译为 '运行时表达式',表示是否支持运行时赋值 ,留着 -->
             <!-- 如果是 true 就表示属性支持动态赋值 -->
            <rtexprvalue>false</rtexprvalue>
        </attribute>
      </tag>
    
    </taglib>
    

    在编写完毕 tld 文件,然后在JSP页面中引入我们的标签库,就可以用我们自定义的标签了 ;


    关于标签处理器类的方法

    这个类里面的方法。并不是向我们上面举的例子那么简单的;上面只是简单的重写了 doStartTag 方法;

    下面,我讲下,什么时候该重写什么方法 ;

    • 获取标签体

      为了获取到标签体,我们需要覆写doStarTag()方法,改变其返回值为EVAL_BODY_BUFFERED;这样标签体就会被当做对象,保存在该标签器类对象中 ; 然后在doEndTag() 里面用 java 代码完成逻辑 ;

      实例:将标签体转成大写字母

      @Override
      public int doStartTag() throws JspException {
          return BodyTagSupport.EVAL_BODY_BUFFERED ;
      }
      
      @Override
      public int doEndTag() throws JspException {
      //        获取标签体
          BodyContent bodyContent = this.getBodyContent() ;
          String content = bodyContent.getString() ;
          content = content.toUpperCase() ;
          try {
              this.pageContext.getOut().write(content);
          } catch (IOException e) {
              throw new RuntimeException(e) ;
          }
          return super.doEndTag();
      }
    • doAfterBody()

      为了控制显示次数,需要利用到 doAfterBody() 这个方法在执行完标签体以后以及执行 doEndTag()方法之前得到调用 ;

      为了确保执行标签体,我们也还需要覆写 doStarTag()方法,改变其返回值;

      实例:控制标签体显示次数

      int i = 5 ;
      @Override
      public int doStartTag() throws JspException {
          return Tag.EVAL_BODY_INCLUDE;
      }
      
      @Override
      public int doAfterBody() throws JspException {
          if (i-- > 0 ){
              return TagSupport.EVAL_BODY_AGAIN;
          }else {
              i = 5 ;
              return TagSupport.SKIP_BODY ;
          }
      
      }
    • 控制标签体是否显示

      取决于d*oStarTag()*方法的方法值
      EVAL_BODY_INCLUDE 包含标签体的意思,会显示标签体 ;而 SKIP_BODY 跳过标签体

        @Override
        public int doStartTag() throws JspException {
            return Tag.SKIP_BODY;
        }
    
    • 是否显示页面

      主要是利用 doEndTag() 方法的返回值 ;
      SKIP_PAGE 代表后续的jsp页面的内容,不再进行处理,也就是不会显示;

        @Override
        public int doEndTag() throws JspException {
            return Tag.SKIP_PAGE;
        }

    自定义标签功能扩展

    1. 控制JSP页面中某一部分的内容是否显示;

      利用是否显示标签体的技术,即可完成 ;

    2. 控制整个页面是否显示;

      做法:在整个页面的外部套个自定义标签,和上面一样 ;

    3. 控制重复输出页面内容

      重写 doAfterBody() 即可;

    4. 修改jsp页面内容输出 ;

      获取代表标签体的对象,对其进行改写,再利用 out 对象输出 ;


    传统标签

    上面我们学的自定义标签技术,属于 传统标签;需要实现的方法比较多,需要我们自己去记忆;

    看下面的图, 左边的是JSP2.0的技术,实现不同的功能需要实现不同的接口;同一个接口中还要选择重写哪些方法

    可以明显的看出来,传统标签 很烦 ,需要我们记住的东西太多了;说实话,笔者在这篇博客的时候,早就忘记了上面的那些知识,也是根据当年的笔记 ,写的这个博客 ;

    Tag接口图


    简单标签

    现在我们学习 简单标签 ,让标签处理器类 继承 SimpleTagSupport 接口;

    简单标签中最重要的两个方法:dotag()setJSPBody();

    setJSPBody() 方法,在服务器执行标签的时候调用,将标签体作为对象传给自定义标签类对象;

    doTag() 方法,服务器执行标签,都会调用这个方法;我们对标签的处理逻辑,都写在这个方法里面 ;


    • 是否显示标签体

      比如,我们想不显示标签体,那么,在这个方法中,就不做任何操作;反之,想显示标签体,就在方法中,获取标签体输出;

      @Override
      public void doTag() throws JspException, IOException {
          //只有doTag方法里面,什么都不做,就不会显示标签体
      }

    • 控制显示样式

      我们需要获取到标签体的字符串形式

      获取方法将标签体输送到StringWriter流的缓冲区中,从缓冲区中获取标签体的内容,从而修改样式

    @Override
        public void doTag() throws JspException, IOException {
    //       获取标签体
            JspFragment jspFragment = this.getJspBody();
    //        将标签体输送到流的缓冲区中
            StringWriter stringWriter = new StringWriter();
    //        这个方法很重要,负责将标签体输送到特定的流里面;
            jspFragment.invoke(stringWriter);
    //        获取缓冲区中的内容
            String s = stringWriter.toString();
            s = s.toUpperCase();
            this.getJspContext().getOut().write(s);
    
        }

    上面代码中最重要的一个方法:jspFragment.invoke(stringWriter);

    参数用于指定将JspFragment对象的执行结果写入到哪个输出流对象中,如果传递给参数的值为 null,则将执行结果写入到 JspContext.getOut()方法返回的输出流 out 对象中(简而言之,可以理解为写给浏览器)


    • 是否显示页面

      不显示页面:在doTage方法中,抛出SkipPageException()异常,方法抛出这个异常,服务器,就不会再计算 剩下 的JSP页面了;

      @Override
      public void doTag() throws JspException, IOException {
          throw new SkipPageException() ;
      }

    配置简单标签

    配置简单标签的时候,配置标签体的时候,需要注意,不能写JSP了,改写为scriptlessSun公司的意思是:以后 脚本表达式 也不要出现在JSP里面了,统统用标签代替 ;

    <tag>
            <name>....</name>
            <tag-class>xxxx.....</tag-class>
            <!-- 传统标签是写JSP,简单标签写 scriptless-->
            <body-content>scriptless</body-content>
        </tag>
    

    与传统标签不同的是,简单标签执行结束以后,创建的对象,并不会像传统标签那样驻留在服务器内存中,而是被垃圾回收器回收掉 ;


    开发带属性的标签

    要想让一个自定义标签具有属性,通常需要完成两个任务:

    1. 在标签处理器中编写每个属性对应的setter方法
    2. tld文件中描述标签的属性

    为自定义标签定义属性的时候,每个属性都必须按照JavaBean的属性命名方式,在标签处理器中定义属性名对应的setter方法,用来接收JSP页面调用自定义标签时传递进来的属性值。

    例如,属性uri,在标签处理器类中就要定义相应的 setUri(String uri)方法。

    在标签处理器中定义响应的set方法后,JSP引擎在解析执行开始标签,也就是doStarTag()方法前,会调用set属性方法,为标签设置属性;

  • 相关阅读:
    用友 t6 凭证http API
    vue 解决 跳转外部地址携带根路径问题
    JavaScript之assign()——对象浅拷贝 (ES6)
    JavaScript之splice 添加或删除元素
    JavaScript之“==”和“===”
    C#——获取阶乘(递归、循环)
    C#——简单的表示两个数中的(三目运算)
    JavaScript 字符串之截取字符串 ——(substring、substr、slice)
    JavaScrpit之Json实现深拷贝
    Vue之this.$forceUpdate——强制更新数据
  • 原文地址:https://www.cnblogs.com/young-youth/p/11665714.html
Copyright © 2020-2023  润新知