zoukankan      html  css  js  c++  java
  • jsp 自定义标签

     

    一. 简单标签处理器

      JSP 2.0的设计者意识到了在JSP 1.1及 JSP1.2中实现 标签及标签处理器的复杂性。因此,JSP 2.0中,在 javax.servlet.jsp.tagext包下增加了接口——SimpleTag。 实现SimpleTag的标签处理器都叫作简单标签处理器; 实现Tag、IterationTag及BodyTag的标签处理器都叫作 经典标签处理器。 简单标签处理器有着简单的生命周期,而且比经典 标签处理器更加易于实现。SimpleTag接口中用于标签 触发的方法只有一个——doTag,并且该方法只会执行 一次。业务逻辑、遍历及页面内容操作都在这里实现。 简单标签处理器中的页面内容都在JspFragment类的实 例中体现。

    简单标签的生命周期如下:

    • JSP容器通过简单标签处理器的无参数构造器创建 它的实例。因此,简单标签处理器必需有无参数构 造器。
    • JSP容器通过setJspContext的方法,传入JspContext 对象:该对象中最重要的方法是getOut,它能返回 JspWriter,通过JspWriter就可以把响应返回前端 了。setJspContext方法的定义如下:
    • public void setJspContext(JspContext jspContext)□

      通常情况下,都需要把使用传入的JspContext指定 为类的成员变量以便后继使用:

    • 如果自定义标签被另一个自定义标签所嵌套,JSP 容器就会调用setParent的方法,该方法的定义如 下:
    • public void setParent(JspTag parent)□
    • JSP容器调用该标签中所定义的每个属性的Set方 法。 

    • 如果需要处理页面内容,JSP容器还会调用 SimpleTag接口的setJspBody方法,把使用 JspFragment封装的页面内容传过来。当然,如果没 有页面内容,那么JSP容器就不会调用该方法。

      javax.servlet.jsp.tagext包中也包含一个SimpleTag的 基础类:SimpleTagSupport。SimpleTagSupport提供了 SimpleTag所有方法的默认实现,并便于扩展实现简单 标签处理器。在SimpleTagSupport类中用getJspContext 方法返回JspContext实例,这个实例在JSP容器调用 SimpleTag的setJspContext方法时传入。

    二.SimpleTag示例

    自定义标签需要有两个步骤:编写标签处 理器及注册标签。

    1. 编写标签处理器

     清单6.1中列出了MyFirstTag类,它是一个 SimpleTag的实现。

      MySimpleTag类中有一个名为jspContext的 JspContext类型变量。在setJspContext方法中,将由JSP 容器中传入的JspContext对象赋给该变量。在doTag方法 中,通过JspContext对象获取JspWriter对象实例。然后 用JspWriter方法中的print方法输出“This is my first tag”的字符串。

    package customtag;
    
    import java.io.IOException;
    import javax.servlet.jsp.JspContext;
    import javax.servlet.jsp.JspException;
    import javax.servlet.jsp.tagext.JspFragment;
    import javax.servlet.jsp.tagext.JspTag;
    import javax.servlet.jsp.tagext.SimpleTag;
    
    public class MyFirstTag implements SimpleTag{
        JspContext jspContext;
        
        public void doTag() throws IOException, JspException{//输出
            System.out.println("dotag");
            jspContext.getOut().print("This is my first tag.");
        }
        public void setParent(JspTag jspTag) {//只有标签被嵌套才会调用
            System.out.println("setParent");
        }
        public JspTag getParent() { 
            System.out.println("getparent");
            return null;
        }
        public void setJspContext(JspContext jspContext) { //jsp容器 首先会调用setJSpContext方法
            System.out.println("setJspcontext");
            this.jspContext = jspContext;
        }
        public void setJspBody(JspFragment body) {//如果标签里面有内容则会调用setJspBody方法,否则不会调用
            System.out.println("setJspBody");
        }
    }

    2.注册标签

      在标签处理器能够被JSP页面使用之前,它需要在 标签库描述器中注册一下,这个描述器是以.tld结尾的 XML文件。本例标签库描述是一个名为mytags.tld的文 件。这个文件必须放在WEB-INF目 录下。

      在标签描述文件中最主要的节点是tag,它用于定 义一个标签。它可以包含一个name节点及一个tag-class 的节点。name节点用于说明这个标签的名称;tag-class 则用于指出标签处理器的完整类名。一个标签库描述器 中可以定义多个标签。

      此外,在标签描述器中还有其他节点。description 节点用于说明这个描述器中的所有标签。tlib-version节 点用于指定自定义标签的版本。short-name节点则是这 些标签的名称。

    <?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 web-jsptaglibrary_2_1.xsd"
        version="2.1">
    
        <description>
            Simple tag examples
        </description>
        <tlib-version>1.0</tlib-version>
        <short-name>My First Taglib Example</short-name> //2.o版本的不能有空格
        <tag>
            <name>firstTag</name>
            <tag-class>customtag.MyFirstTag</tag-class>
            <body-content>empty</body-content> //empty表示标签里面不能有内容
        </tag>
    </taglib>

    2.1 配置部署描述符

    必需在web.xml文件加上以下配置,要不能找不到tld文件的路径

     <jsp-config>
        <taglib>
        <taglib-uri>customtag.MyFirstTag</taglib-uri>
        <taglib-location>/WEB-INF/c.tld</taglib-location>
        </taglib>
     </jsp-config>

    3.使用标签

      要使用自定义标签,就要用到taglib指令。taglib指 令中的uri属性是标签描述器的绝对路径或者相对路 径。本例中使用相对路径。但是,如果使用的是jar包 中的标签库,就必须要使用绝对路径了。

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <%@ taglib uri="/WEB-INF/c.tld"  prefix="easy"   %>
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
    <easy:firstTag></easy:firstTag>
    </body>
    </html>

     

      一旦访问firtTagTest.jsp页面,JSP容器就会调用标 签处理器中的setJspContext方法。由于firstTagTest.jsp中 的标签没有内容,因此JSP容器也就不会在调用doTag 方法前调用setJspBody的方法。

    三. 处理属性

       实现SimpleTag接口或者扩展SimpleTagSupporta的 标签处理器都可以有属性, 下面列出的名为 DataFormaterTag的标签处理器可以将逗号分隔内容转 换成HTML表格。这个标签有两个属性:header、 items。header属性值将会转成表头。 例如, 将“Cities”作为header属性值, “London,Montreal”作为 items属性值,那么会得到如下输出:

    <table style="border:1px solid green">
    <tr><td><b>Cities</b></td></tr>
    <tr><td>London</td></tr>
    <tr><td>Montreal</td></tr>
    </table>

    DataFormatterTag类

    package customtag;
    
    import java.io.IOException;
    import java.util.StringTokenizer;
    import javax.servlet.jsp.JspContext;
    import javax.servlet.jsp.JspException;
    import javax.servlet.jsp.JspWriter;
    import javax.servlet.jsp.tagext.SimpleTagSupport;
    
    public class DataFormatterTag extends SimpleTagSupport {
        private String header;
        private String items;
    
        public void setHeader(String header) {
            this.header = header;
        }
    
        public void setItems(String items) {
            this.items = items;
        }
    
        public void doTag() throws IOException, JspException {
            JspContext jspContext = getJspContext();
            JspWriter out = jspContext.getOut();
            out.print("<table style='border:1px solid green'>
    " + "<tr><td><span style='font-weight:bold'>" + header
                    + "</span></td></tr>
    ");
            StringTokenizer tokenizer = new StringTokenizer(items, ",");//分解字符串
            while (tokenizer.hasMoreTokens()) {
                String token = tokenizer.nextToken();
                out.print("<tr><td>" + token + "</td></tr>
    ");
            }
            out.print("</table>");
        }
    }

     DataFormatterTag类有两个Set方法用于接收属性: setHeader、setItems。doTag方法中则实现了其余的内 容。 doTag方法中,首先通过getJspContext方法获取通 过JSP容器传入的JSPContext对象:

     接着,通过JspContext实例中的getOut方法获取 JspWriter对象,它能将响应写回客户端:

     然后,doTag方法使用StringTokenizer解析items属 性值,然后将每个item都转换成表格中的一行:

      为了能够使用DataFormatterTag的标签处理器,还 需要在tag节点中注册一下,如清单所示。简单地 说,就是把它加入mytags.tld中,用法如下所示。

    <tag>
    <name>dataFormatter</name>
    <tag-class>customtag.DataFormatterTag</tag-class>
    <body-content>empty</body-content>
    <attribute>
    <name>header</name>
    <required>true</required>
    </attribute>
    <attribute>
    <name>items</name>
    <required>true</required>
    </attribute>
    </tag>

    jsp 页面

    <%@ taglib uri="/WEB-INF/c.tld" prefix="easy"%>
    <html>
    <head>
    <title>Testing DataFormatterTag</title>
    </head>
    <body>
    <easy:dataFormatter header="States"
    items="Alabama,Alaska,Georgia,Florida"/>
    <br/>
    <easy:dataFormatter header="Countries">
    <%-- 设置属性items的值 --%>
    <jsp:attribute name="items">
    US,UK,Canada,Korea
    </jsp:attribute>
    </easy:dataFormatter>
    </body>
    </html>

      注意,清单6.6所列出来的JSP页面使用了 dataFormatter标签两次,每次都使用不同的两种方式: 一种是标签属性,另一种是标准属性。

    三.访问标签内容

      在SimpleTag中,可以通过JSP容器传入的 JspFragment来访问标签内容。 JspFragment类提供了多 次访问JSP中这部分代码的能力。JSP片段的定义不能 包含脚本或者脚本表达式,它只能是文件模板或者JSP 标准节点。 JspFragment类中有两个方法:getJspContext、 invoke。我们的定义如下:

    public abstract JspContext getJspContext()
    public abstract void invoke(java.io.Writer writer) throws JspException, java.io.IOException

      getJspContext方法返回这个JspFragment关联的 JspContext对象。可以通过invoke方法来执行这个片段 (标签的内容),然后通过指定的Writer对象把它直接 输出。如果把null传入invoke方法中,那么这个Writer将 会被JspFragment所关联的JspContext对家中的getOut方 法返回的JspWriter方法所接管。

      看清单中所列出来的SelectElementTag类。使用 标签处理器可以输出如下格式的HTML select节点:

    <select>
    <option value="value-1">text-1</option>
    <option value="value-2">text-2</option>
    ...
    <option value="value-n">text-n</option>
    </select>

    在本例中,这些值都是String数组类型countries的 国家名。

    SelectElementTag

    package customtag;
    
    import java.io.IOException;
    import javax.servlet.jsp.JspContext;
    import javax.servlet.jsp.JspException;
    import javax.servlet.jsp.JspWriter;
    import javax.servlet.jsp.tagext.SimpleTagSupport;
    
    public class SelectElementTag extends SimpleTagSupport {
    private String[] countries = {"Australia", "Brazil", "China" };
    
        public void doTag() throws IOException, JspException {
            JspContext jspContext = getJspContext();
            JspWriter out = jspContext.getOut();
            out.print("<select>
    ");
            for (int i = 0; i < 3; i++) {
                // 每次循环设置一次Attribute 然后利用invoke输出
                getJspContext().setAttribute("value", countries[i]);
                getJspContext().setAttribute("text", countries[i]);
                getJspBody().invoke(null); //getJSpBody得到的是标签中间的内容,在这里是 <option value="${value}">${text}</option>
                
            }
            out.print("</select>
    ");
        }
    }

    Tag节点用于注册SelectElementTag, 并把它转成select的标签。接着,像上面的例子一样, 我们继续把这个节点加入到mytags.tld文件中。

    <tag>
    <name>select</name>
    <tag-class>customtag.SelectElementTag</tag-class>
    <body-content>scriptless</body-content> //scriptless代表无脚本
    </tag>

    JSP页面

    <%@ taglib uri="/WEB-INF/mytags.tld" prefix="easy"%>
    <html>
    <head>
    <title>Testing SelectElementFormatterTag</title>
    </head>
    <body>
    <easy:select>
    <option value="${value}">${text}</option>
    </easy:select>
    </body>
    </html>

    注意,select标签传入如下内容:

    <option value="${value}">${text}</option> //该内容可以用getJspBody()获取

    在SelectElementTag标签处理器中的doTag里,每次 触发JspFragment时,都要获取一次value及text属性值:

    for (int i=0; i<3; i++) {
    getJspContext().setAttribute("value", countries[i]);
    getJspContext().setAttribute("text", countries[i]);
    getJspBody().invoke(null); //invoke用于输出
    }

    四. 编写EL函数

     一般来说,编写EL函数需要以下两个步骤:

    (1)创建一个包含静态方法的public类。每个类的 静态方法表示一个EL函数。这个类可以不需要实现任 何接口或者继承特定的类。可以像发布其他任何类一样 发布这个类。这个类必须放在应用中的/WEBINF/classes目录或者它的子目录下。

    (2)用function节点在标签库描述器中注册这个函 数。

    function节点是taglib节点的下级节点,它有如下子 节点:

    • description:可选,标签说明。
    • display-name:在XML工具中显示的缩写名字。
    • icon:可选,在XML工具中使用的icon节点。
    • name:函数的唯一名字。
    • function-class:该函数对应实现的Java类的全名。
    • function-signature:该函数对应实现的Java静态方 法。
    • example:可选,使用该函数的示例说明。
    • function-extension:可以是一个或者多个节点,在 XML工具中使用,用于提供该函数的更多的细节。

      要使用这个函数,须将taglib指令中的uri属性指向 标签库描述,并指明使用的前缀。然后在JSP页面中使 用如下语法来访问该函数:

    <taglib>
        <taglib-uri>customtag.MyFunctions</taglib-uri>
        <taglib-location>/WEB-INF/functionTags.tld</taglib-location>
        </taglib>
    ${prefix:functionName(parameterList)}

    例: MyFunctions类封装了一个静态方法 reverseString。

    package function;
    public class StringFunctions {
    public static String reverseString(String s) {
    return new StringBuffer(s).reverse().toString();
    }
    }

      functiontags.tld文件,它包含描述了 函数名为reverseString的function节点。这个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 web-jsp
    taglibrary_2_1.xsd"
    version="2.1">
    <description>
    Function tag examples
    </description>
    <tlib-version>1.0</tlib-version>
    <function>
    <description>Reverses a String</description>
    <name>reverseString</name>
    <function-class>customtag.MyFunctions</function-class
    >
    <function-signature>
    java.lang.String reverseString(java.lang.String)
    </function-signature>
    </function>
    </taglib>

    使用EL函数

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="utf-8" %>
    <%@ taglib uri="/WEB-INF/functionTags.tld" prefix="f" %>
    <html>
    <head>
    <title>Testing reverseString function</title>
    </head>
    <body>
    ${f:reverseString("Hello World")}
    </body>
    </html>

    五. 发布自定义标签

      可以把自定义的标签处理器以及标签描述器打包到 JAR包里,这样就可以把它发布出来给别人使用了,就 像JSTL一样。这种情况下,需要包含其所有的标签处 理器及描述它们的TLD文件。此外,还需要在描述器中 的uri节点中指定绝对的URI。 例如,在本书附带的app06c应用中,把app06b应用 中的标签及标签器打包在mytags.jar文件中。这个JAR包 的内容如图所示。

    注意这里增加 了uri节点。这个节点的值 是:http://example.com/taglib/function。

    functiontags.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
    web-jsptaglibrary_2_1.xsd"
    version="2.1">
    <description>
    Function tag examples
    </description>
    <tlib-version>1.0</tlib-version>
    <uri>http://example.com/taglib/function</uri>
    <function>
    <description>Reverses a String</description>
    <name>reverseString</name>
    <function-class>function.StringFunction</function-class
    >
    <function-signature>
    java.lang.String reverseString(java.lang.String)
    </function-signature>
    </function>
    </taglib>

      为了在应用中使用这个库,需要把这个JAR文件拷 贝到应用的WEB-INF/lib目录下。在使用的时候,任何 使用自定义标签的JSP页面都要使用这个标签库描述器 中定义的URL。

    <%@ taglib uri="http://example.com/taglib/function" prefix="f"%
    >
    <html>
    <head>
    <title>Testing reverseString function</title>
    </head>
    <body>
    ${f:reverseString("Welcome")}
    </body>
    </html>
  • 相关阅读:
    使用opencv工程
    面试官最爱问的问题背后真相
    哎,哎,去了清华园
    突然发现兰皙欧洗面奶不错
    crs.exe 进程管理里面的流氓进程之封杀
    初步使用OpenCV
    动态网站基础
    Java IO -- 序列化的疑问
    建造模式
    MYSQL 从头开始-2(join)
  • 原文地址:https://www.cnblogs.com/jiangfeilong/p/10704822.html
Copyright © 2011-2022 走看看