JSP-04-JSP标签

JSP标签

JSP 中支持自定义标签。自定义标签是为了简化代码,使用自定义标签替换一个 Java 代码片断,完成相同的功能。

基本用法

下面以“在 JSP 页面中获取并输出客户端 IP”为例,来讲解自定义标签的实现步骤。

# (1) 需求

若使用 Java 代码块来完成这个功能,则 JSP 页面定义如下:

<%
// 获取客户端IP
String clientIP = request.getRemoteAddr();
    out.write(clientIP);
%>

下面要定义一个标签,替换掉这段代码并实现相同功能。

# (2) 定义标签处理器

一个标签实际上对应着一个类中的一段代码。若要定义一个类来完成自定义标签的功能,则需要该类实现一个接口:javax.servlet.jsp.tagext.SimpleTag。这个类称为标签处理器类。该接口中具有五个需要实现的方法:
在这里插入图片描述
 doTag():当 JSP 页面中执行到标签时,由服务器自动调用执行的方法。自定义标签功能的实现都在这个方法中。
 getParent():获取到当前标签的父标签的引用。
 setJspBody():由服务器自动调用。服务器会将 JSP 代码片断传入给当前的 Java 类
 setJspContext():由服务器自动调用。服务器会将 PageContext 对象传入给当前的 Java类。注意,PageContext 是 JspContext 的子类。
 setParent():由服务器自动调用。服务器会将当前标签的父标签引用传入给当前的 Java类。不过,javax.servlet.jsp.tagext.SimpleTagSupport 类实现了 SimpleTag 接口,并且,该类还提供了获取 JSP 片断、获取 PageContext 的方法。
在这里插入图片描述
所以,我们就无需再实现 SimpleTag 接口了,只需要继承 SimpleTagSupport 类即可。

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import java.io.IOException;

public class ClilentSimpleTag extends SimpleTagSupport {
    @Override
    public void doTag() throws JspException, IOException {
       // 获取PageContext对象
        PageContext pa = (PageContext)getJspContext();
        // 获取客户端IP
        String remoteAddr = pa.getRequest().getRemoteAddr();
        // 输出
        pa.getOut().write(remoteAddr);
    }
}
(3) 注册标签处理器

在项目的 WEB-INF 目录下,新建一个扩展名为.tld 的 XML 文件。该文件的约束,同样来自于 Tomcat 的 webapps 中的/examples/WEB-INF/jsp2/jsp2-example-taglib.tld 文件。在其中添加相应的子标签。
`

<?xml version="1.0" encoding="UTF-8" ?>

<tlib-version>1.0</tlib-version>
<short-name>mytage</short-name>
<uri>http://www.xxx.com/jsp/mytage</uri>

<tag>
    <name>clientIp</name>
    <tag-class>com.jsp.tag.ClilentSimpleTag</tag-class>
    <body-content>empty</body-content>
</tag>
` 以上配置就定义了一个前辍为 mytag,标签为 clientIp 的标签,该标签没有标签体。
(4) 使用自定义标签

在 JSP 页面中,只需通过 taglib 指令,将相应的标签库导入即可。

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%--引入标签库--%>
<%@taglib uri="http://www.xxx.com/jsp/mytage" prefix="mytag"%>
<html>
<body>
<%-- 自定义标签库的使用 --%>
<mytag:clientIp/>
</body>
</html>
(5) 标签处理器实例的生命周期

标签处理器是多例的。每执行一次自定义标签,都会创建一个标签处理器的实例。该标签执行完毕,则标签处理器实例被销毁。可以在标签处理器中添加一个无参构造器。会发现,每刷新一次页面,均会创建一个标签处理器实例。

public class ClilentSimpleTag extends SimpleTagSupport {
    @Override
    public void doTag() throws JspException, IOException {
       // 获取PageContext对象
        PageContext pa = (PageContext)getJspContext();
        // 获取客户端IP
        String remoteAddr = pa.getRequest().getRemoteAddr();
        // 输出
        pa.getOut().write(remoteAddr);
    }

    public ClilentSimpleTag() {
    System.out.println("实例化标签无参构造!");
    }
}
定义带标签体的标签

若要定义带标签体的标签,则需要注意以下几点:
 使用 getJspBody()方法可以获取到标签的文本对象,此文本对象以 JspFragment 对象形式出现。
 JspFragment 对象具有一个 invoke(Writer)方法,该方法可以将标签文本对象写入到输出流中。
 若要获取字符串类型的标签文本数据,则需要将 JspFragment 对象写入到一个具有缓存功能的输出流中,且该流数据还可以被截取到。一般这个输出流使用 StringWriter。

## (1)需求

<%--自定义的标签使用 --%>

<mytag:toUpperCase>dfdfg</mytag:toUpperCase>

<%-- 存在于四大域值 --%>
<%
  session.setAttribute("sum","jhjgh");
%>

<mytag:toUpperCase>${sessionScope.sum}</mytag:toUpperCase>

定义一个标签,实现的功能是,将标签体文本中小写字母转换为大写字母。若标签体文本来自于表达式的计算结果,则先计算表达式,再将结果转换为大写。在前面项目中直接实现。

(2) 定义标签处理器
package com.jsp.tag;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import java.io.IOException;
import java.io.StringWriter;

public class UpperCaseTage extends SimpleTagSupport {
    @Override
    public void doTag() throws JspException, IOException {
       // 创建缓存输出流
        StringWriter stringWriter = new StringWriter();
        // 获取JSP片断 即标签文本对象

        JspFragment jspBody = this.getJspBody();

        // 将标签写入到输出流中 ,
        jspBody.invoke(stringWriter);

        // 获取标准输出流对象

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

        // 将所有标签文本放到缓存流中

        String s = stringWriter.toString();

        //转为大写

        String s1 = s.toUpperCase();

        // 输出到游览器

        out.write(s1);
    }
}

(3) 注册标签处理器修改 WEB-INF 下的 customTagLib.tld 文件。在其中添加如下配置。

 <tag>
        <name>toUpperCase</name>
        <tag-class>com.jsp.tag.UpperCaseTage</tag-class>
        <body-content>scriptless</body-content>
    </tag>

对于标签,其值有四个选项:
 empty:表示自定义的标签没有标签体
 scriptless:表示自定义的标签具有标签体,且标签体文本中不能包含 Java 脚本,即不能包含 Java 代码段与 Java 表达式。但可以包含 EL 表达式,且会对 EL 表达式进行计算。
 JSP:在 JSP2.0 之前,定义标签处理器类需要实现 Tag 接口,或继承自 TagSupport 类。
那时候该属性值有用,表示会将标签体的文本内容原样显示在浏览器。该属性值对于继承自 SimpleTagSupport 类的标签处理器是不能使用的。使用会报错。
 tagdependent:表示会将标签体的文本内容原样显示在浏览器。若其中包含 EL 表达式,也会将其作为普通字符串,而不对其进行计算。

(4) 运行结果

通过前面需求中 JSP 页面中的内容,其运行结果如下:
在这里插入图片描述

(5) 其它情况

若将 customTagLib.tld 中该中的值设置为 tagdependent

 <tag>
        <name>toUpperCase</name>
        <tag-class>com.jsp.tag.UpperCaseTage</tag-class>
        <body-content>tagdependent</body-content>
    </tag>

则以上 JSP 页面的运算结果会是,将所有内容全部作为普通字符串,即使 EL 表达式,也不会进行计算。
在这里插入图片描述

定义带属性的标签

若要定义带属性的标签,则需要注意以下几点:
 标签中的属性,反映到标签处理器中,就是一个具有 set 方法的属性。
 对于 JspFragment 的 invoke(Writer)方法需要注意,若指定了输出流,则会将标签文本对象写入到指定的输出流中。若没有指定输出流,即指定输出流为 null,则默认将标签文本对象写入到 JSP 标准的输出流 JspWriter 中。

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%--引入标签库--%>
<%@taglib uri="http://www.xxx.com/jsp/mytage" prefix="mytag"%>
<html>
<body>

<%
  session.setAttribute("testTrue", true);
%>

<mytag:if test="${sessionScope.testTrue}"></mytag:if>

</body>
</html>

定义一个标签,实现的功能是:若 test 属性的值为 true,则显示标签体的文本。否则不显示。其中 test 的值可以来自于表达式的计算结果。

(2) 定义标签

处理器标签处理器中声明的具有 set 方法的属性,其属性名即为标签的属性名。

package com.jsp.tag;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import java.io.IOException;

public class IfSimpleTag extends SimpleTagSupport {

    private Boolean test;

    public void setTest(Boolean test) {
        this.test = test;
    }


    @Override
    public void doTag() throws JspException, IOException {
        if (test) {
            // 获取PageContext对象
            PageContext pageContext = (PageContext) this.getJspContext();
            // 获取输出流
            JspWriter out = pageContext.getOut();
            // 将文本写出到输出流
            this.getJspBody().invoke(out);
        }
    }

}

以上代码完全等价于下面的代码:

package com.jsp.tag;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import java.io.IOException;

public class IfSimpleTag extends SimpleTagSupport {

    private Boolean test;

    public void setTest(Boolean test) {
        this.test = test;
    }

    @Override
    public void doTag() throws JspException, IOException {
        if (test) {
            this.getJspBody().invoke(null);
        }
    }

    //    @Override
//    public void doTag() throws JspException, IOException {
//        if (test) {
//            // 获取PageContext对象
//            PageContext pageContext = (PageContext) this.getJspContext();
//            // 获取输出流
//            JspWriter out = pageContext.getOut();
//            // 将文本写出到输出流
//            this.getJspBody().invoke(out);
//        }
//    }

}
(3) 注册标签处理器

修改 WEB-INF 下的 customTagLib.tld 文件。在其中添加如下配置。

 <tag>
        <name>if</name>
        <tag-class>com.jsp.tag.IfSimpleTag</tag-class>
        <body-content>scriptless</body-content>
        <attribute>
            <name>test</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
    </tag>

若自定义标签具有属性,则需要在中添加子标签。
 :指定属性名。要保证与标签处理器中的属性名相同。
 :指定该属性在标签中是否是必须的,为 true 则为必须的,为 false 则可有可无。
 :指定该属性值是否可以来自于运行时表达式的值,为 true 表示属性值可以来自表达式,为 false 则表示该值只能为常量。这里的表达式指的是 EL 表达式与 JSP中的表达式块。rtexprvalue,是 Runtime Expression Value 的简写。

定义 forEachList 标签

(1) 需求
<%@ page import="java.util.ArrayList" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%--引入标签库--%>
<%@taglib uri="http://www.xxx.com/jsp/mytage" prefix="mytag" %>
<html>
<body>

<%
    ArrayList<String> names = new ArrayList<>();
    names.add("小草");
    names.add("小化");
    names.add("小奴役");
    names.add("小密集");
    pageContext.setAttribute("names", names);
%>

<mytag:forEachList items="${names}" var="name">
    ${name} <br>
</mytag:forEachList>

</body>
</html>

定义一个标签,实现的功能是:循环遍历属性 items 所指定的 list,当前遍历的元素名称为 var 属性所指定的名称,使用 EL 在标签体中输出当前元素的值。

(2) 定义标签处理器
public class ForEachListSimpleTag extends SimpleTagSupport {

    private List items;

    private String var;

    @Override
    public void doTag() throws JspException, IOException {
        PageContext pageContext = (PageContext)this.getJspContext();
        for (Object obj : items ) {
           // 循环将数据写到pageContext域空间中
            pageContext.setAttribute(var,obj);
            // 获取当前标签文本对象 将其写到标准输出流中
            this.getJspBody().invoke(null);
            
        }

    }



    public List getItems() {
        return items;
    }

    public void setItems(List items) {
        this.items = items;
    }

    public String getVar() {
        return var;
    }

    public void setVar(String var) {
        this.var = var;
    }


}

需要注意以下几点:
 var 定义为 String,而非 Object。因为我们需要的是将 var 作为域属性名称放入到域属性空间,而具体的元素,是作为域属性空间中的值出现的。
 在循环遍历时,需要将当前循环元素存放到域属性空间中。因为 JSP 页面中是通过 EL读取每一个 List 的元素的。所以每一个 List 的元素,均需要存放到域属性空间中。
 每循环遍历一次,都需要将当前的标签文本对象放入到 JSP 的标准输出流中,以显示于JSP 页面中。

(3) 注册标签处理器
 <tag>
        <name>forEachList</name>
        <tag-class>com.jsp.tag.ForEachListSimpleTag</tag-class>
        <body-content>scriptless</body-content>
        
        <attribute>
            <name>items</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>

        <attribute>
            <name>var</name>
            <required>true</required>
            <rtexprvalue>false</rtexprvalue>
        </attribute>
    </tag>

注册标签处理器时需要注意:
 属性 items 的值允许来自表达式,所以的值为 true。
 属性 var 的值不允许是动态的,所以的值为 false。

需求

前面定义的 forEachList 标签,只能循环遍历 List 集合。但对于 Set、Map 集合,及数组均不能进行遍历。这里要定义一个标签 forEach,可以用来遍历 Set、Map 集合,及 Object数组。

自行完成

将自定义标签库打包发行

将前面的自定义标签库打为 Jar 包后,就可以在今后的项目中使用了。具体操作百度。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值