欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Tomcat与JavaWeb 8.2 自定义标签的创建和使用(二)重复执行标签主体、访问标签主体内容

程序员文章站 2022-05-30 21:19:11
...

1.    创建和使用iterate标签(重复执行标签主体)

JSP网页中经常需要显示集合中的批量数据。我们曾使用"<%%>"形式的Java程序片段的for循环等来完成这种功能。

下面将创建一个iterate标签,它能完成和Java程序片段的循环类似的功能。下面例程的iterate.jsp中就通过iterate标签来遍历books集合中的所有元素。使用iterate标签可以使JSP代码更加简洁,提高可读性。

iterate.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="mm" uri="/mytaglib" %>
<%@ page import="java.util.*" %>
<%@ page import="mypack.BookDetails" %>
<%
    BookDetails book1 = new BookDetails("Name1","Title1",100,"Description1");
    BookDetails book2 = new BookDetails("Name2","Title2",200,"Description2");
    BookDetails book3 = new BookDetails("Name3","Title3",300,"Description3");
    BookDetails book4 = new BookDetails("Name4","Title4",400,"Description4");
    List books = new ArrayList();
    books.add(book1);
    books.add(book2);
    books.add(book3);
    books.add(book4);
%>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <table border="1">
        <caption><b>书的信息</b></caption>
        <tr>
            <th>作者</th>
            <th>书名</th>
            <th>价格</th>
            <th>读者评价</th>
        </tr>
        <mm:iterate var="book"  items="<%=books%>">
        <tr>
            <td>${book.name}</td>
            <td>${book.title}</td>
            <td>${book.price}</td>
            <td>${book.description}</td>
        </tr>
        </mm:iterate>
    </table>
</body>
</html>

 iterate标签有两个属性:

  • var:指定存放在页面范围内的属性名,此处为“book”,意味着iterate标签的处理类每次从集合中取出一个元素后,会把这个元素存放在页面范围内,属性名为“book”。
  • items:表示待遍历访问的集合,此处为books集合。

iterate标签的处理类是IterateTag:

mypack/IterateTag.java

package mypack;


import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.TagSupport;
import java.util.Collection;
import java.util.Iterator;


public class IterateTag extends TagSupport {
    private Iterator items;
    private String var;
    private Object item;


    public void setItems(Collection items) {
        if (items.size()>0){
            this.items = items.iterator();
        }
    }


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


    @Override
    public int doStartTag() throws JspException {
        if (items.hasNext()) {
            item = items.next();
            saveItem();
            System.out.println("save +1 次");
            return EVAL_BODY_INCLUDE;
        } else {
            return SKIP_BODY;
        }
    }


    @Override
    public int doAfterBody() throws JspException {
        if (items.hasNext()) {
            item = items.next();
            saveItem();
            System.out.println("save +1 次");
            return EVAL_BODY_AGAIN;
        } else {
            return SKIP_BODY;
        }
    }


    private void saveItem() {
        if (item == null) {
            pageContext.removeAttribute(var, PageContext.PAGE_SCOPE);
        } else {
            pageContext.setAttribute(var,item);
        }
    }
}
当Servlet容器调用iterate.jsp的iterate标签时,运行IterateTag对象的处理流程如下:
  1. 调用setPageContext()、setParent()、setVar()、setItems()方法。
  2. 调用doStartTag()方法,由于items枚举对象不为空,因此取出一个元素,把它存放在页面范围内,属性名为变量var的值“book”。
  3. 执行标签主体,打印页面范围内的名为“book”的BookDetails对象的name等属性。
  4. 执行doAfterBody()方法,由于tems枚举对象不为空,因此取出一个元素,把它存放在页面范围内,属性名为变量var的值“book”。
  5. 重复执行标签主体和doAfterBody()方法,直到items枚举对象为空,此时doStartTag()会返回一个SKIP_BODY,结束循环。

BookDetails.java(mypack目录下)

package mypack;


public class BookDetails {
    private String name;
    private String title;
    private float price;
    private String description;
    public BookDetails(String name,String title,float price,String description){
        this.name = name;
        this.title = title;
        this.price = price;
        this.description = description;
    }


    public String getName() {
        return name;
    }


    public float getPrice() {
        return price;
    }


    public String getDescription() {
        return description;
    }


    public String getTitle() {
        return title;
    }
}

在mytaglib.tld文件中对iterate标签进行定义:

    <tag>
        <name>iterate</name>
        <tag-class>mypack.IterateTag</tag-class>
        <body-content>JSP</body-content>
        <attribute>
            <name>var</name>
            <required>true</required>
            <rtexprvalue>false</rtexprvalue>
        </attribute>
        <attribute>
            <name>items</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
    </tag>

由于在iterate标签的主体中包含如${book.name}等JSP代码,因此把content-type元素设置为“JSP”。由于iterate标签的items属性是一个基于<%=%>形式的Java表达式,因此它的相应<rtexprvalue>子元素的值为true。

OK,现在运行应用,访问localhost:8080/iterate.jsp:

Tomcat与JavaWeb 8.2 自定义标签的创建和使用(二)重复执行标签主体、访问标签主体内容,可以看到,实现了重复执行标签主体内容的功能。

2.    创建和使用greet标签(访问标签主体内容)

greet.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="mm" uri="/mytaglib" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%int size = 3;%>
<mm:greet count="3">
    <font size="<%=size++%>">
        Hi,${param.username} <br>
    </font>
</mm:greet>
</body>
</html>

greet标签有一个count属性,它决定了重复执行标签主体的次数。greet标签的处理类为GreetTag类,该类继承BodyTagSupport类,具有重复执行标签主体和访问缓存在BodyContent对象中的标签主体的执行结果的作用。

mypack/GreetTag.java

package mypack;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyContent;
import javax.servlet.jsp.tagext.BodyTagSupport;
import java.io.IOException;

public class GreetTag extends BodyTagSupport {
    private int count;
    private String username;

    public void setCount(int count) {
        this.count = count;
    }

    @Override
    public int doStartTag() throws JspException {
        System.out.println("Call doStartTag()");

        if (count > 0) {
            return EVAL_BODY_BUFFERED;
        } else {
            return SKIP_BODY;
        }
    }

    @Override
    public void setBodyContent(BodyContent b) {
        System.out.println("Call setBodyContent()");

        super.setBodyContent(b);
    }

    @Override
    public void doInitBody() throws JspException {
        System.out.println("Call doInitBody()");
        username = pageContext.getRequest().getParameter("username");
    }

    @Override
    public int doAfterBody() throws JspException {
        System.out.println("Call doAfterBody()");

        if (count > 1) {
            count--;
            return EVAL_BODY_AGAIN;
        } else {
            return SKIP_BODY;
        }
    }

    @Override
    public int doEndTag() throws JspException {
        System.out.println("Call doEndTag()");
        JspWriter out = bodyContent.getEnclosingWriter();
        try {
            String content = bodyContent.getString();  //得到标签主体的执行结果
            System.out.println(bodyContent.getString());

            //修改标签主体的执行结果
            if (username != null && username.equals("Monster")) {
                content = "Go away.Monster!";
            }

            out.println(content);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return EVAL_PAGE;
    }
}
当Servlet容器处理greet.jsp中的greet标签时,运行GreetTag对象的主要流程如下:
  1. 调用setPageContext()、setParent()、setCount()方法。
  2. 调用doStartTag()方法,由于count属性大于0,因此返回EVAL_BODY_BUFFERED。
  3. 调用setBodyContent()和doInitBody()方法。
  4. 执行标签主体,把标签主体的执行结果缓存在BodyContent对象中。
  5. 执行doAfterBody()方法,由于count属性大于1,返回EVAL_BODY_AGAIN。
  6. 重复执行标签主体和doAfterBody()方法,直到count属性不再大于1.
  7. 调用doEndTag()方法,读取username请求参数,如果username为“Monster”,就向客户端输出“Go away,Monster!”,否则就输出在BodyContent对象的缓存中的数据。

在mytaglib.tld文件中对greet标签的定义:

 <tag>
        <name>greet</name>
        <tag-class>mypack.GreetTag</tag-class>
        <body-content>JSP</body-content>
        <attribute>
            <name>count</name>
            <required>true</required>
            <rtexprvalue>false</rtexprvalue>
        </attribute>
    </tag> 

由于在greet标签主体中包含如${paran.username}等JSP代码,因此把<body-content>元素设置为“JSP”。

配置完毕后,运行Web应用,访问localhost:8080/greet.jsp?username=Tomcat,页面显示:
Tomcat与JavaWeb 8.2 自定义标签的创建和使用(二)重复执行标签主体、访问标签主体内容
观察IDEA控制台的输出:
Tomcat与JavaWeb 8.2 自定义标签的创建和使用(二)重复执行标签主体、访问标签主体内容

以上打印结果显示了Servlet容器调用GreetTag对象的各个方法的流程,其中doAfterBody()方法调用了3次。上图下半部分的结果为3次重复执行标签主体的结果,这些结果都先缓存在了BodyContent对象中,最后由doEndTag()方法一次性地把BodyContent对象中的数据全部输出到客户端。 

再访问localhost:8080/greet.jsp?username=Monster:
Tomcat与JavaWeb 8.2 自定义标签的创建和使用(二)重复执行标签主体、访问标签主体内容
控制台输出:
Tomcat与JavaWeb 8.2 自定义标签的创建和使用(二)重复执行标签主体、访问标签主体内容

可以看到,输入Monster也会有对应的3个主体标签缓存在BodyContent对象中,但是它们最后没有被打印到网页。

对于第2小节,如果理不清逻辑,应该回到 8.1 JSP Tag API ,重新温习IterationTag和BodyTag的各个方法的执行逻辑。