Java Web之自定义JSP标签
write:2022-5-7

上文:Java Web之使用过滤器,
前面我们学习了JSP自带的标签:Java web之JSP技术详解,但自带标签数量有限,从JSP1.1开始出现了自定义JSP标签技术,允许用户创建客户化的标签,从而完成特定需求的功能。


文章目录

  • 1. 理解自定义JSP标签的作用
  • 2. 掌握创建并运用自定义JSP标签的步骤
  • 3. 了解JSP Tag API
  • 3.1 标签的处理类
  • 3.2 TagSupport类
  • 3.3 PageContext类
  • 3.4 doStartTag()方法的返回值
  • 3.5 doEndTag()方法的返回值
  • 3.6 用户自定义的标签属性
  • 4. Servlet容器处理自定义标签的流程
  • 4.1 范例1:创建hello标签
  • 4.2 范例2:创建message标签
  • 5. 练习题


1. 理解自定义JSP标签的作用

自定义JSP标签技术是在JSP 1.1版本中才出现的,它支持用户在JSP文件中自定义标签,这样可以使JSP代码更加简洁。
自定义标签是可重用的
自定义标签能处理复杂的逻辑运算和事务,或者定义JSP网页的输出内容和格式。

2. 掌握创建并运用自定义JSP标签的步骤

因为自定义的JSP标签可以被重复使用,因此创建自定义JSP标签和在JSP文件中使用自定义JSP标签是独立的;

  1. 创建自定义JSP标签
    (1)创建标签的处理类(决定标签完成的功能)
    (2)创建XML格式的标签库描述文件
  2. 在JSP文件中使用自定义JSP标签
    (1)在web.xml文件中声明所引用的自定义标签所在的标签库(标签库:组织和管理一组功能相关的标签)
    (2)在JSP文件中引入标签库,然后插入标签,例如:< mm:hello/>

3. 了解JSP Tag API

3.1 标签的处理类

Servlet容器执行JSP文件时,如果遇到自定义标签,就会调用这个标签的处理类。

自定义word模板 Java javaweb自定义标签_自定义word模板 Java


标签处理类必须扩展以下两个类之一

javax.servlet.jsp.TagSupport或者javax.servlet.jsp.BodyTagSupport

下面主要结束如何扩展TagSupport类

3.2 TagSupport类

  1. TagSupport类的主要方法
    doStartTag():Servlet容器遇到自定义标签的起始标志(即:<)时调用该方法
    doEndTag():Servlet容器遇到自定义标签的结束标志(即:/>)时调用该方法
    setValue(String k,Object o):在标签处理类中设置key/value
    getValue(String k): 在标签处理类中根据参数key返回匹配的value
    removeValue(String k):在标签处理类中删除key/value
  2. TagSupport类的两个重要属性
    (1)两个属性
    parent:引用嵌套了当前标签的上层标签(父标签)的处理类对象
    pageContext:引用当前JSP页面的javax.servlet.jsp.PageContext对象
    (2)与属性对应的方法
    setPageContext(PageContext pc):设置PageContext对象,该方法由Servlet容器在调用doStartTag或doEndTag方法前调用
    setParent(Tag t):设置嵌套了当前标签的上层标签的处理类,该方法由Servlet容器在调用doStartTag()方法前调用
    getParent():返回嵌套了当前标签的上层标签的处理类对象

Servlet容器在调用doStartTag() 方法前,会先调用setPageContext()和setParent()方法,设置pageContext和parent属性。

自定义word模板 Java javaweb自定义标签_前端_02


parent属性是private类型的,在doStartTag()或doEndTag()方法中可以通过getParent()方法获取上层父标签的处理类对象;

在TagSupport类中定义了protected类型的pageContext成员变量,因此在标签处理类中可以直接访问pageContext变量。

3.3 PageContext类

TagSupport类的两个重要属性之一:PageContext类
PageContext类本身由Servlet容器创建,它是Servlet容器为JSP页面提供的得力助手,在JSP页面的自定义标签也可以使用这个得力助手;

PageContext类提供了保存和访问Web应用的各个范围内的共享数据的方法:

public void setAttribute(String name, Object value, int scope)//存放
public Object getAttribute(String name, int scope)//访问

其中,scope参数用来指定属性存在的范围,它的可选值包括:
PageContext.PAGE_SCOPE
PageContext.REQUEST_SCOPE
PageContext.SESSION_SCOPE
PageContext.APPLICATION_SCOPE
例如:

pageContext.setAttribute(“username”,”Tom”, PageContext.SESSION_SCOPE);//存放
String username=(String)pageContext.getAttribute(“username”, PageContext.SESSION_SCOPE);//访问

3.4 doStartTag()方法的返回值

TagSupport类的主要方法之一:doStartTag():Servlet容器遇到自定义标签的起始标志(即:<)时调用该方法

doStartTag()方法返回一个整数值,用来决定程序的后续流程。它有两个可选值:
Tag.SKIP_BODY表示标签之间的内容被忽略。
Tag.EVAL_BODY_INCLUDE表示标签之间的内容被正常执行。

例如对于以下代码:
< prefix: Mytag>
Hello
……
……
< /prefix:Mytag>

假若< Mytag>的doStartTag()方法返回Tag.SKIP_BODY,”Hello”字符串不会显示在网页上;若返回Tag.EVAL_BODY_INCLUDE,“Hello” 字符串将显示在网页上。

3.5 doEndTag()方法的返回值

TagSupport类的主要方法之一:doEndTag():Servlet容器遇到自定义标签的结束标志(即:/>)时调用该方法

doEndTag()方法也返回一个整数值,用来决定程序后续流程。它有两个可选值:
Tag.SKIP_PAGE:表示立刻停止执行JSP网页,网页上未处理的静态内容和JSP程序代码均被忽略,任何已有的输出内容立刻返回到客户的浏览器上。
Tag.EVAL_PAGE:表示按正常的流程继续执行JSP网页。

3.6 用户自定义的标签属性

在标签中还能包含自定义的属性,例如:
< prefix:mytag username=“Tom">
……
……
< /prefix:mytag>

在标签处理类中应该将这个属性作为成员变量,并且分别提供设置和读取属性的方法;
假定以上username为String类型,可以定义如下方法:
private String username;
public void setUsername(String value){
this.username=value;
}
public String getUsername(){
return username;
}

4. Servlet容器处理自定义标签的流程

自定义word模板 Java javaweb自定义标签_标签库_03

4.1 范例1:创建hello标签

定义一个名为mytaglib的标签库,它包含一个简单的hello标签,这个标签能够将JSP页面中所有的< mm:hello/>解析为字符串“hello”。

自定义word模板 Java javaweb自定义标签_前端_04


hello标签的处理类HelloTag

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.tagext.TagSupport;

public class HelloTag extends TagSupport{
  /** 当Servlet容器遇到hello标签的结束标志时,调用此方法 */
  public int doEndTag() throws JspException{
    try{
      //打印字符串“Hello”
      pageContext.getOut().print("Hello");
    }catch (Exception e) {
      throw new JspTagException(e.getMessage());
    }
    return EVAL_PAGE;
  }
}

创建hello标签的标签库的描述文件
创建Tag Library的描述文件mytaglib.tld文件,在这个文件中定义mytaglib标签库和hello标签。这个文件存放位置为/WEB-INF/mytaglib.tld。

<taglib>
  <tlib-version>1.0</tlib-version>//标签库版本
  <jsp-version>1.1</jsp-version>//jsp版本
  <short-name>mytaglib</short-name>//标签库默认的前缀名
  <uri>/mytaglib</uri>//标签库唯一的访问标识符

  <tag>//定义一个标签
    <name>hello</name>//标签名
    <tag-class>mypack.HelloTag</tag-class>//标签处理类
    <body-content>empty</body-content>//标签主体的类型
    <info>Just Says Hello</info>//标签的说明信息
  </tag>

</taglib>

如果某个web应用需要使用名为mytaglib的标签库中的标签,就需要在web应用中加入标签库的描述文件mytaglib.tld文件:把mytaglib.tld文件放在/WEB-INF下,目录/WEB-INF/mytaglib.tld;
在web.xml文件中加入< taglib>元素

<web>
     ……
    <taglib>//声明web应用引用的标签库
       <taglib-uri>/mytaglib</taglib-uri>//为标签库映射唯一一个标识符,根据标识符引用标签库
       <taglib-location>/WEB-INF/mytaglib.tld</taglib-location>//标签库tld文件存放位置
    </taglib>

<web>

在JSP文件中加入hello标签
(1)在hellowithtag1.jsp中加入引用mytaglib的taglib指令:<%@ taglib uri=“/mytaglib” prefix=“mm” %>
以上taglib指令中,prefix属性用来指定引用mytaglib标签库时的前缀(所有以mm开头的标签都来自mytaglib标签库)。

(2)在hellowithtag1.jsp文件中插入hello标签:

< b>< mm:hello/> :

<%= request.getParameter(“username") %>

自定义word模板 Java javaweb自定义标签_前端_05


访问hellowithtag1.jsp:

http://localhost:8080/hellowithtag1.jsp?username=weiqin

自定义word模板 Java javaweb自定义标签_开发语言_06

4.2 范例2:创建message标签

创建一个能替换helloapp应用中JSP网页的静态文本的标签,这个标签名为message,它放在mytaglib标签库中。

自定义word模板 Java javaweb自定义标签_开发语言_07


在hellowithtag2.jsp文件中使用message标签的代码如下:

< b>< mm:message key=“hello.hello” /> :

<%= request.getAttribute(“username") %>< /b>

当客户访问hello.jsp网页时,message标签的处理类会根据属性key的值从一个文本文件中找到与key匹配的字符串。假定这个字符串为“Hello”,然后将这个字符串输出到网页上。

创建包含JSP网页静态文本的文件

首先将创建包含JSP网页静态文本的文件,这些文本以key=value的形式存放,这个文件名为messageresource.properties:

自定义word模板 Java javaweb自定义标签_java_08


在Web应用启动时装载静态文本

尽管装载静态文本的任务可以直接由标签处理类来完成,但是把初始化的操作安排在Web应用启动时完成,这更符合Web编程的规范。

在本例中,由LoadServlet类的init方法负责从静态文本文件中读取静态文本,然后把它们装载到Properties对象中,最后再把这个Properties对象作为属性保存到ServletContext中。

自定义word模板 Java javaweb自定义标签_前端_09


LoadServlet类的init方法

public void init()throws ServletException {
    
    Properties ps=new Properties();
    try{
      ServletContext context=getServletContext();
      InputStream in=
         context.getResourceAsStream("/WEB-INF/messageresource.properties");
      ps.load(in);
      in.close();

      //在Web应用范围内存放包含静态文本的Properties对象
      context.setAttribute("ps",ps);  
    }catch(Exception e){
      e.printStackTrace();
    }
  }

为了保证在Web应用启动时就加载LoadServlet,应该在web.xml中配置这个Servlet时设置load-on-startup属性:

<servlet>
        <servlet-name>load</servlet-name>
        <servlet-class>mypack.LoadServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

创建MessageTag标签处理类
MessageTag包含一个成员变量key,它与message标签的属性key对应。在MessageTag中定义了getKey和setKey方法:

private String key=null;
    public String getKey(){
      return this.key;
}
public void setKey(String key){
      this.key=key;
} 
public int doEndTag() throws JspException {

     try {
	   Properties    ps=
        (Properties)pageContext.getAttribute("ps",pageContext.APPLICATION_SCOPE);
         String message=(String)ps.get(key);
         pageContext.getOut().print(message);
      } catch (Exception e) {
            throw new JspTagException(e.getMessage());
      }
       
         return EVAL_PAGE;
    }
}

在mytaglib库中定义message标签

<tag>//定义一个标签
    <name>message</name>//标签名
    <tag-class>mypack.MessageTag</tag-class>//标签处理类
    <body-content>empty</body-content>//标签主体的类型
    <info>produce message by key</info>//标签的说明信息
 
    <attribute>
        <name>key</name>//属性
        <required>true</required>//属性是否需要赋值
    </attribute>
  </tag>

5. 练习题

  1. 问题:在标签处理类中,如何访问session范围内的共享数据?
    选项:
    (A)在TagSupport类中定义了session成员变量,直接调用它的getAttribute()方法即可。
    (B)在标签处理类TagSupport类中定义了pageContext成员变量,先通过它的getSession()方法获得当前的HttpSession对象,再调用HttpSession对象的getAttribute()方法。
    ©pageContext.getAttribute(“attributename”,PageContext.SESSION_SCOPE)
    答案: B,C
  2. 问题:在下面的选项中,哪些是TagSupport类的doStartTag()方法的有效返回值?
    选项:
    (A) Tag.SKIP_BODY
    (B) Tag.SKIY_PAGE
    © Tag.EVAL_BODY_INCLUDE
    (D) Tag.EVAL_PAGE
    答案: A,C
  3. 问题:标签处理类的doStartTag()方法以及doEndTag()方法由谁调用?
    选项:
    (A) HTTPServletRequest类
    (B) HTTPServletResponse类
    © ServletContext类
    (D) Servlet容器
    答案: D