JSP 2开发自定义标签

JSP2开发自定义标签

目录

1. 开发自定义标签的步骤

JSP2的规范简化了标签库的开发,在原本的JSP1.1中开发自定义标签比较复杂。
JSP2中开发自定义标签只需要如下的几步:

  1. 开发自定义标签处理类。
  2. 建立一个*.tld文件,每个文件对应一个标签库,每个标签库可以包含多个标签。
  3. 在JSP文件中使用自定义标签(测试)。

2. 开发简单的自定义标签

1. 自定义标签处理类

自定义标签类继承一个父类:javax.servlet.tagext.SimpleTagSupport,除此之外,JSP自定义标签还有如下的要求:

  • 对于所有的标签类的属性,都要有对应的gettter和setter方法。
  • 重写doTag()方法,这个方法负责生成页面内容。

下面是一个简单的输出HelloWorld的标签。

public class HelloWorldTag extends SimpleTagSupport {

    // 重写doTag()方法,该方法文便签生成标签页
    @Override
    public void doTag() throws JspException, IOException {
        // 获取页面输出流,并输出字符
        getJspContext().getOut().write("Hello World "
                + new java.util.Date());
    }
}

上面的标签处理类继承了SimpleTagSupport父类,并重写doTag()方法,而doTag()输出页面的内容。该标签页面没有提供属性,因此无需提供getter和setter方法。

2. 标签处理类的声明周期

  1. JSP容器通过调用其无参构造,创建一个简单的标签处理类实例。因此,简单的标签处理器必须有一个无参构造器。
  2. JSP容器调用setJspContext方法,同时传递一个JSPContext对象。JspContext最重要的方法时getOut,它返回一个JspWriter,用于将相应发送到客户端。setJspContext方法的签名如下:
    public void setJspContext(JspContext jspContext)
    大多数时候,会需要将传进的JspContext付一个类变量,以便后续使用。
  3. 如果表示标签处理类的定制标签是嵌套在另一个标签中的,JSP容器就会调用setParent方法。
  4. JSP容器为改标签的定义的每个属性都调用设置方法(setter)
  5. 如果标签中有主体内容,JSP将调用SimpleTag接口的setJspBody方法,将主体内容作为JspFragment。如果没有主体内容,则JSP容器不会调用这个方法。
  6. JSP容器调用doTag方法,所有变量在doTag方法返回是进行同步。

3. 建立TLD文件。

首先,何为TLD?TLD是Tag Library Definitation的缩写,即标签库文件的定义,文件的后缀是tld,每个TLD文件对应一个标签库,一个标签库中可以包含多个标签。TLD文件也被称为标签库定义文件。每个标签都需要在该文件中注册,类似于Servlet在web.xml中注册。
标签库文件的根元素是taglib,他可以包含多个tag子元素,每一个子元素定义一个标签。Tomcat的Webapps\examples\WEB-INF\jsp2路径下有一个jsp2-example-taglib.tld文件,该文件是一个TLD文件的示例。
可以将该文件复制到WEB-INF/路径下,或者其它的子路径下,复制并修改后的内容如下:

<?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">
        <tlib-version>1.0</tlib-version>
        <short-name>mytaglib</short-name>
        <uri></uri>
</taglib>

标签库文件也是一个标准的XML文件,该文件的根元素是taglib元素。
可以在这个文件中注册自定义的标签。
从上面的代码中可以看出taglib有如下的3个子元素。

  • tlib-version:指的是该库的版本,这是一个座位标识的内部版本号。
  • short-name:该标签的默认的短名,该名称通常没有太大的作用。
  • uri:这个属性非常的重要,他指定了该标签库的URI,相当于标签库的唯一标识。JSP页面中使用标签库时就是根据该URI来定位标签库的。URI有两种路径:
    • 相对路径:可以在该文件不使用URI,而在JSP文件中直接定位到该文件。(使用自定义标签时会说到)
    • 绝对路径:使用网址+路径作为统一资源定位,这个在发布自定义标签时非常重要。

所以,在练习是使用相对路径。当然如果有域名也可以使用绝对路径,如果不想使用相对路径,有没有域名,可以使用http://www.example.com,这是一个可以做测试的一个域名。
打开之后:
这里写图片描述

翻译过来大概是:该域被建立用于文档中的说明性示例。您可以在没有事先协调或要求许可的情况下使用此域。

4. 注册标签

在taglib还可以包含多个tag元素,每个tag元素可以定义一个标签,tag元素下允许出现如下常用元素:

  • name:该标签的名称,在JSP页面中就是根据该名称来使用此标签的。
  • tag-class:指定标签的处理类,他指定了标签有那个标签处理类来处理。
  • body-content:指定标签体的内容。该元素的值可以是如下几个。
    • tagdependent:指定标签处理类自己负责处理标签体。
    • empty:指定该标签只能作为空标签使用。
    • scriptless:指定该标签的标签体可以是静态的HTML元素、表达式语言(EL),但不允许出现JSP脚本。
  • dynamic-attributes:指定该标签是否支持动态属性。只有当定义动态属性标签时才是用该元素。
    下面的程序注册了hello标签,所使用的类是前面出现的类:
<?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">
        <tlib-version>1.0</tlib-version>
        <short-name>mytaglib</short-name>
        <uri>http://www.example.com/mytaglib</uri>
    <tag>
        <!-- 标签的名字 -->
        <name>hello</name>
        <!-- 标签的处理类 -->
        <tag-class>com.example.mytagClass.HelloWorldTag</tag-class>
        <!-- 定义标签体 -->
        <body-content>empty</body-content>
    </tag>
</taglib>

5. 使用标签库

在JSP页面中使用标签库需要注意如下两点:

  1. 标签库的URI:确定使用哪个标签库。
  2. 标签名:确定使用那个标签。

使用标签库导入标签库,使用taglib编译指令导入标签库,就是将标签库和指定的前缀关联起来。
taglib编译指令的语法如下:
<%@ taglib prefix="tagPrefix" uri="tagliburi" %>
其中uri属性指定标签库的URI,这个URI可以确定一个标签库。而prefix属性指定标签库的前缀,即所有使用该前缀的标签将由该标签库处理。
使用标签的语法如下:

<tagPrefix:tagName tagAttribute="tagValue"...>
    <tagBody/>
</tagPrefix:tagName>

如果该标签没有标签体,则可以使用如下的语法:

<tagPrefix:tagName tagAttribute="tagValue".../>

上面使用的标签的语法都使用了标签体,而hello标签没有任何属性,所以该标签只需要使用<mytag:hello/>即可。
如下:

<%@ page contentType="text/html;charset=UTF-8"
         language="java" %>
<%@taglib prefix="hello" uri="http://www.example.com/taglib" %>
<%-- 如果前面没有指定URI,则可以这样使用 --%>
<%-- <%@taglib prefix="hello" uri="/WEB-INF/mytaglib.tld" %> --%>
<html>
<head>
    <title>Hello Tag</title>
</head>
<body>
Hello!!!<br/>
<hello:helloWorld></hello:helloWorld>
</body>
</html>

运行之后可以看到如下效果:
这里写图片描述

3. 带属性的标签

前面说的标签即没有属性,也没有标签题,用法、功能都比较简单。
下面的就是一个带有属性的标签:

import javax.servlet.jsp.JspContext;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import java.io.IOException;
import java.util.StringTokenizer;

public class DataFormatterTag extends SimpleTagSupport {

    private String header;
    private String item;

    public String getHader() {
        return header;
    }

    public void setHeader(String header) {
        this.header = header;
    }

    public String getItem() {
        return item;
    }

    public void setItem(String item) {
        this.item = item;
    }

    @Override
    public void doTag() throws JspException, IOException {
        JspContext jspContext = getJspContext();
        JspWriter out = jspContext.getOut();
        out.println("<table style='border:1px solid green'>\n"
             + "<tr><td><span style='font-weight:blod'>"
            + header + "</span></td></tr>");
        StringTokenizer tokenizer = new StringTokenizer(item, ",");
        while (tokenizer.hasMoreTokens()) {
            String token = tokenizer.nextToken();
            out.print("<tr><td>" + token + "</td><tr>\n");
        }
        out.print("</table>");
    }

}

前面说过每个属性都必须设置setter和getter,所以上面的header和item都有getter和setter方法,要开发的这个标签的作用是,获取一个表头和item生成一个表格。其中item是一个用“,”隔开的字符串,然后使用StringTokenizer解析成items属性。
接下来就是注册该标签了.
对有属性的标签,需要在<tag…/>元素增加<attribute…/>子元素,每个attribute子元素定义了一个标签属性。<attribute…/>子元素通常还需用定义如下的几个元素。

  • name:设置属性名,则元素的值是字符串的内容。
  • reqired:设置该属性是否必须属性,该子元素的值true或false。
  • fragment:设置该属性是否支持JSP脚本、表达式等动态语言、子元素的值是true或false。

下面的是为上面的DataFormatterTag注册,在mytablib.tld文件中增加如下配置片段。

<tag>
    <name>dataFormatter</name>
    <tag-class>com.example.myTag.DataFormatterTag</tag-class>
    <body-content>empty</body-content>
    <attribute>
        <name>header</name>
        <required>true</required>
        <fragment>false</fragment>
    </attribute>
    <attribute>
        <name>items</name>
        <required>true</required>
        <fragment>false</fragment>
    </attribute>
</tag>

上面的代码分别配置了header和items属性。
下面就是使用该标签了。

<%@ page contentType="text/html;charset=UTF-8"
         language="java" %>
<%@taglib prefix="simple" uri="/WEB-INF/mytaglib.tld" %>
<html>
<head>
    <title>测试DataFormatterTag</title>
</head>
<body>
<simple:dataFormatter header="字母" items="A,B,C,D"/>
<br/>
<simple:dataFormatter header="国家">
    <jsp:attribute name="items">
        美国,中国,加拿大
    </jsp:attribute>
</simple:dataFormatter>
</body>
</html>

上面的代码使用了两次的dataFormatter标签,并且以两种不同的方式传递属性,一种是标签属性,一种使用的attribute标准动作指令。
下面的是他的效果:
这里写图片描述

4. 带有标签体的标签

带有标签体的标签可以切入其他内容。通常用于完成一些逻辑运算,例如循环和判断等。下面以一个迭代器标签为示例,介绍下。
首先定义一个标签处理类:

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

public class IteratorTag extends SimpleTagSupport {
    // 标签属性,用于指定需要被迭代的集合
    private String collection;
    // 标签属性,指定被迭代的集合元素,为集合元素指定名称
    private String item;

    public String getCollection() {
        return collection;
    }

    public void setCollection(String collection) {
        this.collection = collection;
    }

    public String getItem() {
        return item;
    }

    public void setItem(String item) {
        this.item = item;
    }

    // 处理标签的方法,在标签处理类中只需要重写doTag()方法

    @Override
    public void doTag() throws JspException, IOException {
        // 从page scope中获取名为collection的集合
        Collection itemList = (Collection)getJspContext().getAttribute(collection);
        // 遍历集合
        for (Object s : itemList) {
            // 将集合元素设置到page范围内
            getJspContext().setAttribute(item, s);
            // 输出标签体
            getJspBody().invoke(null);
        }
    }
}

上面的标签处理类与之前的类基本一样,只是在遍历collection的时就调用getJspBody()方法,该方法返回标签所包含的标签体:JspFragment对象,执行invoke()方法,即可输出标签体的内容。该标签的作用是:每遍历一个集合元素,即输出标签体一次。
因为标签体不为空,所以配置是指定body-content为sctiptless,注册该标签的代码如下:

<tag>
        <!-- 定义标签名 -->
        <name>iterator</name>
        <!-- 定义标签处理类 -->
        <tag-class>com.example.myTag.IteratorTag</tag-class>
        <!-- 定义标签体不允许出现JSP脚本 -->
        <body-content>scriptless</body-content>
        <!-- 配置标签属性:collection -->
        <attribute>
            <name>collection</name>
            <required>true</required>
            <fragment>true</fragment>
        </attribute>
        <!-- 配置标签属性 -->
        <attribute>
            <name>item</name>
            <required>true</required>
            <fragment>true</fragment>
        </attribute>
    </tag>
    <tag>

上面的代码中指定该标签体可以是静态的HTML内容,也可以是表达式语言,但不允许是JSP脚本。
为了测试在JSP页面中的使用效果,下面将一个List对象设置成page范围的属性,然后该标签来迭代输出LIst集合的全部元素。

<%@ page contentType="text/html;charset=UTF-8"
         language="java" %>
<%@taglib prefix="mytag" uri="/WEB-INF/mytaglib.tld"%>
<html>
<head>
    <title>测试带标签体的标签</title>
</head>
<body>
<h2>带有标签体的标签-迭代器标签</h2>
<%
    // 创建一个List对象
    List<String> a = new ArrayList<String>();
    a.add("中国");
    a.add("美国");
    a.add("英国");
    // 将List对象放进page范围
    pageContext.setAttribute("a", a);
%>
<table border="1" bgcolor="#7fffd4" width="300">
    <!-- 使用迭代器标签,对a集合进行迭代 -->
    <mytag:iterator item="item" collection="a">
        <tr>
            <td>${pageScope.item}</td>
        </tr>
    </mytag:iterator>
</table>
</body>
</html>

下面是该便签的测试效果图:
这里写图片描述

5. 以页面片段作为属性标签

JSP2规范的自定义标签还允许直接将一段“页面片段”作为属性,这种方式给自定义标签提供了更大的灵活性。
以“页面片段”为属性的标签与普通的标签区别并不大,在该标签处理类定义一个JSPFragment类型的属性,即表明该标签允许使用“页面片段”类型的属性。

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;

public class FragmentTag extends SimpleTagSupport {
    private JspFragment fragment;

    public JspFragment getFragment() {
        return fragment;
    }

    public void setFragment(JspFragment fragment) {
        this.fragment = fragment;
    }

    @Override
    public void doTag() throws JspException, IOException {
        JspWriter out = getJspContext().getOut();
        out.println("<div style='padding:10px;border:1px solid black;"
                + ";border-radius:20px'>");
        out.println("<h3>下面是动态传入的JSP片段</h3>");
        // 调用、输出“页面片段”
        fragment.invoke(null);
        out.println("</div>");
    }
}

上面程序中定义的fragment成员变量,该成员变量代表了使用该标签时的“页面片段”。所以注册是与普通并无任何区别。

<tag>
        <!-- 定义标签名 -->
        <name>fragment</name>
        <!-- 定义标签处理类 -->
        <tag-class>com.example.myTag.FragmentTag</tag-class>
        <!-- 指定标签不支持标签体 -->
        <body-content>empty</body-content>
        <!-- 定义标签属性:fragment -->
        <attribute>
            <name>fragment</name>
            <required>true</required>
            <fragment>true</fragment>
        </attribute>
    </tag>

由于该标签需要一个fragment属性,该属性的类型是JspFragment,因此使用该标签时需要使用<jsp:attribute…/>动作指令来设置属性值,如下所示:

<%@ page contentType="text/html;charset=UTF-8"
         language="java" %>
<%@taglib prefix="mytag" uri="/WEB-INF/mytaglib.tld" %>
<html>
<head>
    <title>测试:以页面片段作为标签属性</title>

</head>
<body>
<h2>下面显示的是自定义标签中的内容</h2>
<mytag:fragment>
    <jsp:attribute name="fragment">
    <%-- 使用后jsp:attribute标签传入fragment参数(改注释不能放在fragment内) --%>
        <mytag:helloWorld/>
    </jsp:attribute>
</mytag:fragment>
<br/>
<mytag:fragment>
    <jsp:attribute name="fragment">
        <!-- 下面是动态的JSP页面 -->
        ${pageContext.request.remoteAddr}
    </jsp:attribute>
</mytag:fragment>
</body>
</html>

由于程序制定fragment标签的标签体为empty,因此程序中国fragment开始和Fragment结束标签之间只能使用<jsp:attribute…/>子元素,甚至注释都不行。
效果如下图所示:
这里写图片描述

6. 动态属性标签

有时候标签的属性个数是不确定的,属性名也不确定,这时候就需要借助动态属性标签了。
动态属性标签有两个额外的要求:

  • 标签的处理类还需要实现DynAttribute接口
  • 配置便签时通过<dynamic-attributes…/>指定该标签所支持的动态属性。

下面是一个动态属性标签处理类。

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.DynamicAttributes;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import java.io.IOException;
import java.util.ArrayList;

public class DynaAttributesTag extends SimpleTagSupport
        implements DynamicAttributes {
    // 保存每个属性名的集合
    private ArrayList<String> keys = new ArrayList<>();
    // 保存每个属性值的集合
    private ArrayList<Object> values = new ArrayList<>();

    @Override
    public void doTag() throws JspException, IOException {
        JspWriter out = getJspContext().getOut();
        // 此处只是简单的输出每个属性值
        out.println("<ol>");
        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            Object value = values.get(i);
            out.println("<li>" + key + " = " + value + "</li>");
        }
        out.println("</ol>");
    }

    @Override
    public void setDynamicAttribute(String uri, String localName, Object value) throws JspException {
        // 添加属性名
        keys.add(localName);
        // 添加属性值
        values.add(value);
    }
}

上面的标签属性处理类实现了DynamicAttribute接口,该方法用于为该标签处理类动态的添加属性名和属性值。标签的属性名和属性值分别私用ArrayList<String>类型的keys和ArrayList<Object>类型的values保存。
注册该标签时需要额外的指定<dynamic-attributes…/>子元素,来表明该标签是动态属性的标签。下面是该属性的注册代码片段:

<tag>
        <name>dynaAttr</name>
        <tag-class>com.example.myTag.DynaAttributesTag</tag-class>
        <body-content>empty</body-content>
        <!-- 指定支持动态属性 -->
        <dynamic-attributes>true</dynamic-attributes>
    </tag>

一旦定义了动态属性的标签,接下来在页面中使用该属性时将十分的灵活,完全可以在为该标签设置任意的属性。如下:

<%@ page contentType="text/html;charset=UTF-8"
         language="java" %>
<%@taglib prefix="mytag" uri="/WEb-INF/mytaglib.tld" %>
<html>
<head>
    <title>测试动态属性的标签</title>
</head>
<body>
<h2>下面显示的是自定义标签的内容</h2>
<h4>指定两个属性</h4>
<mytag:dynaAttr name="你的名字" age="10000"/>
<h4>指定4个属性</h4>
<mytag:dynaAttr name="星武者" age="20" work="暂无" birthday="****年**月**日"/>
</body>
</html>

下面是该标签的效果:

这里写图片描述

7. 源码

想要获得源码点击下方按钮:
按钮

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值