Head First JSP---随笔八(传统标记)

传统标记处理器

也许我们很幸运,我们的公司采用了JSP 2.0。但也可能很糟糕,用的是2.0以前的版本。


Tag处理器API

我们只要扩展其中一个类就行了。
这里写图片描述


一个非常小传统标记处理器

这个例子太极除了,它与SimpleTag处理器的doTag()方法几乎没有什么区别。
这里写图片描述


有两个方法的传统标记处理器

这里写图片描述


标记有体时:简单标记和传统标记的比较

这里写图片描述
这里思考一个问题:如果我们要循环一个体呢?这里是return,我们该如何做呢?后面有答案!


传统标记的生命周期是不同的

这里写图片描述
这样的生命周期很有意思。


传统标记生命周期取决于返回值

doStartTag()和doEndTag()方法返回一个int。这个int告诉容器下一步要做什么。

对于doStartTag(),容器的问题是“我该计算体吗?”。

对于doEndTag(),容器的问题是“我要继续计算调用页面的余下部分吗?”。

如图:
这里写图片描述


IterationTag允许体重复执行

TagSupport的UML:
这里写图片描述
通过重写doAfterBody()方法达到重复执行体的功能

例子:
简单标记实现:
这里写图片描述
传统标记实现:
这里写图片描述


TagSupport的默认返回值

如果没有覆盖返回一个整数的TagSupport生命周期方法,要当心TagSupport方法实现返回的默认值
这里写图片描述


需要注意的点,给你一个例子

要注意,doAfterBody()方法在已经处理一次体后才开始运行,看看上面的调用“流程图”
这里写图片描述
这里还要注意:容器可以重用传统标记处理器,也就是说,我们要注意全局变量
这里写图片描述


实例

还记得第三章的啤酒案例吗?现在来实现动态获取!

开发环境:
这里写图片描述

初始化页面(init.jsp):

<%@ page import="java.util.*" %>>
<html>
<body>
    <%  List<String> optionsList = new ArrayList<String>();
        optionsList.add("light");
        optionsList.add("amber");
        optionsList.add("brown");
        optionsList.add("dark");
        getServletContext().setAttribute("colorList", optionsList);
    %>
    <jsp:forward page="beer.jsp"/>
</body>
</html>

我们的表单页面(beer.jsp):

<%@ taglib prefix="formTags" uri="http://example.com/tags/forms" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<body>
    <form method="post" action="SelectBeer.do">
        <p>Select beer characteristics:</p>
        Color:
        <c:catch>
            <formTags:select name="color" size="1"
                optionsList="${applicationScope.colorList}"/>
            <br><br>
        </c:catch>
        <input type="submit" value="submit">
    </form>
</body>
</html>

我们的简单标记处理器类(SelectTagHandler.java):

package com.example.taglib;

import java.io.IOException;
import java.util.List;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.SimpleTagSupport;

public class SelectTagHandler extends SimpleTagSupport{
    private List<String> optionsList;
    private String name;
    private int size;
    public void setOptionsList(List<String> optionsList) {
        this.optionsList = optionsList;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void setSize(int size) {
        this.size = size;
    }

    @Override
    public void doTag() throws JspException, IOException {
        PageContext pageContext = (PageContext) getJspContext();
        JspWriter out = pageContext.getOut();
        out.println("<select name='"+name+"' size='"+size+"'>");
        for(int i=0;i<optionsList.size();i++) {
            String s = optionsList.get(i);
            out.println("<option value='"+s+"'> "+s+" </option>");
        }
        out.println("</select>");
    }
}

我们的标记文件(demo.tld):

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE taglib
    PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
            "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_2.dtd">

<taglib>
    <tlib-version>1.2</tlib-version>
    <jsp-version>1.2</jsp-version>
    <short-name>Forms Taglib</short-name>
    <uri>http://example.com/tags/forms</uri>
    <description>
        An example tab library of replacements for the HTML form tags.
    </description>
    <tag>
        <name>select</name>
        <tag-class>com.example.taglib.SelectTagHandler</tag-class>
        <body-content>empty</body-content>

        <attribute>
            <name>optionsList</name>
            <type>java.util.List</type>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>

        <attribute>
            <name>name</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>

        <attribute>
            <name>size</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>

    </tag>
</taglib>

我们的部署文件(DD):

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    id="WebApp_ID" version="3.0">

    <welcome-file-list>
        <welcome-file>init.jsp</welcome-file>
    </welcome-file-list>

</web-app>

实验结果:
这里写图片描述


select标记还不完善

我们HTML的<select>标记有更多的属性
这里写图片描述
我们需要在标记处理器类添加些能够设置它们的方法:
这里写图片描述
看完之后…呵呵,tld也要配置这些属性。太恐怖了!!

幸好!JSP规范专门针对这个目的提供了一个API,那就是我们的DynamicAttributes接口
这里写图片描述


标记处理器代码使用DynamicAttribute接口

使用3张图搞定这个部分:

首先看看setXxx()该如何解决(因为这里是键/值对,所以用HashMap是最好的选择,当然也可以用别的):
这里写图片描述
接下来是doTag()方法:
这里写图片描述
最后是TLD中的配置:
这里写图片描述
最后就完成了。这简直是神器

这里指出:这个接口对传统标记也是适用的


标记文件的dynamic-attributes

这里要注意,tagAttrs是一个页面作用域变量
这里写图片描述
一个实例:

首先是.tag文件:

<%@ tag body-content="empty" dynamic-attributes="tagAttrs"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:forEach var="attr" items="${tagAttrs}">
        ${attr.key} = ${attr.value}
</c:forEach>

其次是测试的.jsp文件:

<%@ taglib prefix="myTags" tagdir="/WEB-INF/tags" %>
<html>
<body>
    <myTags:header name="123" value="111"/>
</body>
</html>

实验结果:
这里写图片描述
帅呆了!!


DynamicAttributes接口的要点

  1. DynamicAttributes接口允许标记处理器类接收任意数目的标记属性。
  2. TLD中的标记声明必须包含<dynamic-attribute>元素。
  3. 显式标记属性必须有一个设置方法。
  4. 一般会使用setDynamicAttribute()方法利用一个hashmap存储动态属性名/值对。
  5. 标记文件也可以使用动态属性。
  6. 标记要使用tag指令的dynamic-attributes属性。
  7. dynamic-attributes的值包含动态属性的一个hashmap。
  8. 一般地,会使用JSTL forEach定制动作迭代处理这个映射。

如果要访问体内容!

如果我们能直接访问具体的体内容,就可以做很多事情了,比如在表达式中使用体内容,或者以某种方式过滤或修改。为此可以扩展BodyTagSupport而不是TagSupport,这样你就能访问BodyTag接口方法。
这里写图片描述


基于BodyTag,你会获得两个新方法

注意:默认值改变为EVAL_BODY_BUFFERED
这里写图片描述
还有一个需要注意的地方(如果没有体,需要return SKIP_BODY):
这里写图片描述


传统标记方法的生命周期返回值

这里写图片描述


标记可以调用其父标记

SimpleTag和Tag都有一个getParent()方法(注意返回值存在区别)。
这里写图片描述


简单标记的父标记可以是传统标记

简单标记的父标记可以是传统标记的原因,是因为Tag is-like-a JspTag。因此,简单标记不能是传统标记的父标记
这里写图片描述


父标记从子标记获取信息

我们知道,只有getParent()方法而没有getChild()方法,也就是说,只能获取到父标记不能得到子标记。那我们如何获取到信息呢?

一个例子:
这里写图片描述
这样就很巧妙的让父标记知道子标记的信息。下面是测试:
这里写图片描述
结果与我们想象的一样


得到一个任意的祖先

这里写图片描述
使用findAncestorWithClass()方法向上查找到一个特定的祖先,返回第一个找到的祖先(也就是如果这颗树有多个我们要查找的节点,返回深度最深的节点并且这个节点是this的祖先)。


简单标记和传统标记的重要区别

这里写图片描述


使用标记处理器的PageContext API(复习)

这里写图片描述


本章完。。

猜你喜欢

转载自blog.csdn.net/qq_37340753/article/details/81433899