前面的博客,我们讲过了 自定义 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的技术,实现不同的功能需要实现不同的接口;同一个接口中还要选择重写哪些方法

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


简单标签

现在我们学习 简单标签 ,让标签处理器类 继承 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属性方法,为标签设置属性;

(十三)自定义JSTL标签的更多相关文章

  1. 自定义JSTL标签和函数库

    一.自定义JSTL标签 1.编写标签处理类: (1)实现 SimpleTag 接口,通过 setJspContext()方法可以获取到 jspContext 对象,实际上也是 pageContext ...

  2. 如何自定义JSTL标签与SpringMVC 标签的属性中套JSTL标签报错的解决方法

    如何自定义JSTL标签 1.创建一个类,从SimpleTagSupport继承 A) 通过继承可以获得当前JSP页面上的对象,如JspContext I) 实际上可以强转为PageContext II ...

  3. 自定义EL函数、自定义JSTL标签

    自定义EL函数 1.做一个类(静态) package com.maya.el; public class ELBiaoDaoShi { public static String TiHuan(Stri ...

  4. 自定义jstl标签库

    开发环境:Spring+SpringMVC +Maven +Mybatis JSTL 标签库的配置: 导入对应的 jstl.jar 和 standard.jar ,我使用的配置如下: <prop ...

  5. 自定义jstl标签*

    原文链接:https://www.it610.com/article/442039.htm 步骤如下: 1.写tld文档:用来指定标签的名字,标签库等. 2.写标签处理器类. 3.配置到web.xml ...

  6. 自定义jstl标签开发

    从jsp1.1开始就可以在jsp页面中使用自定义标签了,使用自定义标签不但可以实现代码重用,而且可以使jsp代码更简洁.Jsp2.0的标签扩展API中又增加了SimpleTag接口和其实现类Simpl ...

  7. EL表达式、JSTL标签库

    一.EL(Expression Language)表达式 语法结构:${var} 若要停用对EL表达式的评估的话,需要使用page指令将isELIgnored属性值设为true: <%@ pag ...

  8. JSP学习笔记(3)——JSTL 标签库

    JSP Standard Tag Lib,名为JSP标准标签库,设计的目的主要用来方便我们将数据输出,而不是使用JSP中的语法<% %> <%= %> <%! %> ...

  9. 12 自定义标签/JSTL标签库/web国际化/java web之设计模式和案例

    EL应用      自定义一个标签,实现两个字符串的相加 1回顾      1.1servlet生命周期           init(ServletConfig)           service ...

随机推荐

  1. 【转】使用 Ansible 实现数据中心自动化管理

    长久以来,IT 运维在企业内部一直是个耗人耗力的事情.随着虚拟化的大量应用.私有云.容器的不断普及,数据中心内部的压力愈发增加.传统的自动化工具,往往是面向于数据中心特定的一类对象,例如操作系统.虚拟 ...

  2. jquery validate 表单验证插件 代码

    /* 表单验证 */ var signupFormValidator = $("#signupForm").validate({ /* 自定义验证规则 */ rules : { o ...

  3. 小程序开发--WePy框架

    现如今mvvm框架如此火热,其核心思想即js逻辑层不直接操作DOM,只改变组件状态:而视图层则通过模板template进行渲染. 1.WePy项目的目录结构 ├── dist 小程序运行代码目录 ├─ ...

  4. json页面解析

    List<TjfxDTO> cyjbList = new ArrayList<TjfxDTO>(); cyjbList=tjfxService.cyjb_wcjd(tjfxDT ...

  5. 使用SQL中的update更新多个字段值

    使用SQL中的update更新多个字段值,set后面的条件要用逗号不能用and set后面的多个条件之间没有关联也不可以有关联,所以就不能用and了:where 条件后面 可以为and 如: upda ...

  6. Java 方法与数组

    方法 什么是方法? 方法定义:方法(Method),函数(function),其实就指一个特定的功能操作,程序中完成独立功能,可重复使用的一段代码的集合. 方法的定义 方法定义格式: [修饰符] 返回 ...

  7. REST架构原则初探

    目录 什么是RESTful架构? REST 架构原则 资源(Resource) 表现层(Representation) 状态转换(State Transfer) 无状态通信原则 RESUful API ...

  8. redis之为什么redis是单线程?

    官方FAQ表示,因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽.既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的 ...

  9. SQL-W3School-测验:SQL 测验

    ylbtech-SQL-W3School-测验:SQL 测验 1.返回顶部 1. 您可以通过 W3SCHOOL 的测验程序来测试您的 SQL 技能. 关于本测验 本测验包含 20 道题,每道题的最长答 ...

  10. mxnet深度学习实战学习笔记-9-目标检测

    1.介绍 目标检测是指任意给定一张图像,判断图像中是否存在指定类别的目标,如果存在,则返回目标的位置和类别置信度 如下图检测人和自行车这两个目标,检测结果包括目标的位置.目标的类别和置信度 因为目标检 ...