如何开发自定义标签

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何开发自定义标签相关的知识,希望对你有一定的参考价值。

一、简介

原理:用户自定义的 jsp 标记。当一个含有自定义标签的 jsp 页面被 jsp 引擎编译成 servlet 时,tag 标签被转化成了对一个标签处理器类的对象的操作。

标签库API:定义在 javax.servlet.jsp.tagext 中

技术分享

 

二、实现SimpleTag接口的标签处理器类的生命周期

1)setJspContext:Jsp 引擎将代表 JSP 页面的 pageContext 对象传递给标签处理器对象

2)setParent:Jsp 引擎将父标签处理器对象传递给当前标签处理器对象。只有存在父标签时,Jsp 引擎才会调用才方法

3)setXxx:设置标签属性。只有定义属性才调用该方法。

4)setJspBody:若存在标签体,Jsp 引擎将把标签体封装成一个 JspFragment 对象,调用 setJspBody 方法将 JSPFragment 对象传递给标签处理器对象。若标签体为空,这 setJspBody 将不会被 Jsp 引擎调用。

5)doTag:容器调用标签处理器对象的 doTag 方法执行标签逻辑

 

三、自定义标签开发与应用步骤

  • 用Java类来实现标签功能(类中的字段和属性就是使用标签时的属性。就像 a 标签的 href 属性)
  • 用TLD文件描述标签的名字、类型以及该标签所关联的java类等

1. 编写完成标签功能的 Java 类(标签处理器)

HelloSimpleTag(实现SimpleTag接口):把 value 输出 count 遍

public class HelloSimpleTag implements SimpleTag {

    private String value;
    private String count;
    
    public void setValue(String value) {
        this.value = value;
    }
    
    public void setCount(String count) {
        this.count = count;
    }
    
    //标签体逻辑实际应该编写到该方法中. 
    @Override
    public void doTag() throws JspException, IOException {
//        System.out.println("value: " + value  + ", count: " + count);
//        
//        HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();
//        pageContext.getOut().print("Hello: " + request.getParameter("name"));
        
        JspWriter out = pageContext.getOut();
        int c = 0;
        c = Integer.parseInt(count);
        for(int i = 0; i < c; i++){
            out.print((i + 1) + ": " + value);
            out.print("<br>");
        }
    }

    @Override
    public JspTag getParent() {
        System.out.println("getParent");
        return null;
    }

    @Override
    public void setJspBody(JspFragment arg0) {
        System.out.println("setJspBody");
    }

    private PageContext pageContext;
    
    //JSP 引擎调用, 把代表 JSP 页面的 PageContext 对象传入
    //PageContext 可以获取 JSP 页面的其他 8 个隐含对象. 
    //所以凡是 JSP 页面可以做的标签处理器都可以完成. 
    @Override
    public void setJspContext(JspContext arg0) {
        System.out.println(arg0 instanceof PageContext);  
        this.pageContext = (PageContext) arg0;
    }

    @Override
    public void setParent(JspTag arg0) {
        System.out.println("setParent");
    }
}

 

 

ReadFileTag(继承SimpleTagSupport类):给一个文本路径,输出该文本里的内容

public class ReadFileTag extends SimpleTagSupport{

    //相对于当前 WEB 应用的根路径的文件名
    private String src;

    public void setSrc(String src) {
        this.src = src;
    }
    
    @Override
    public void doTag() throws JspException, IOException {
        PageContext pageContext = (PageContext) getJspContext();
        InputStream in = pageContext.getServletContext().getResourceAsStream(src);
        BufferedReader reader = new BufferedReader(new InputStreamReader(in)); 
        
        String str = null;
        while((str = reader.readLine()) != null){
            str = Pattern.compile("<").matcher(str).replaceAll("&lt");
            str = Pattern.compile(">").matcher(str).replaceAll("&gt");
            
            pageContext.getOut().println(str);
            pageContext.getOut().println("<br>"); 
        }
    }
}

 

 

 

 

2. 编写标签库描述(tld)文件,在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 http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
    version="2.0">

    <!-- 描述 TLD 文件 -->
    <description>MyTag 1.0 core library</description>
    <display-name>MyTag core</display-name>
    <tlib-version>1.0</tlib-version>

    <!-- 建议在 JSP 页面上使用的标签的前缀 -->
    <short-name>mytag</short-name>
    <!-- 作为 tld 文件的 id, 用来唯一标识当前的 TLD 文件, 多个 tld 文件的 URI 不能重复. 通过 JSP 页面的 taglib 
        标签的 uri 属性来引用. -->
    <uri>http://www.xxx.com/mytag/core</uri>

    <!-- 描述自定义的 HelloSimpleTag 标签 -->
    <tag>
        <!-- 标签的名字: 在 JSP 页面上使用标签时的名字 -->
        <name>hello</name>

        <!-- 标签所在的全类名 -->
        <tag-class>com.zhang.javaweb.tag.HelloSimpleTag</tag-class>
        <!-- 标签体的类型 -->
        <body-content>empty</body-content>

        <!-- 描述当前标签的属性 -->
        <attribute>
            <!-- 属性名 -->
            <name>value</name>
            <!-- 该属性是否被必须 -->
            <required>true</required>
            <!-- rtexprvalue: runtime expression value 当前属性是否可以接受运行时表达式的动态值 -->
            <rtexprvalue>true</rtexprvalue>
        </attribute>

        <attribute>
            <name>count</name>
            <required>false</required>
            <rtexprvalue>false</rtexprvalue>
        </attribute>
    </tag>

<tag> <name>readerFile</name> <tag-class>com.zhang.javaweb.tag.ReadFileTag</tag-class> <body-content>empty</body-content> <attribute> <name>src</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag>
</taglib>

 

 

3. 在JSP页面中导入和使用自定义标签

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<!-- 导入标签库(描述文件) -->
<%@taglib uri="http://www.xxx.com/mytag/core" prefix="mytag" %>
    
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
    <mytag:readerFile src="/WEB-INF/note.txt" />
    
<!-- count不能接收运行时表达式的值,所以不能用el表达式 --> <mytag:hello value="${param.name }" count="10" /> </body> </html>

 

 

三、使用方法总结

1. 带标签体的自定义标签

1). 若一个标签有标签体

<mytag:testJspFragment>abcdefg</mytag:testJspFragment>

在自定义标签的标签处理器中使用 JspFragment 对象封装标签体信息。

2). 若配置了标签含有标签体

则 JSP 引擎会调用 setJspBody() 方法把 JspFragment 传递给标签处理器类
在 SimpleTagSupport 中还定义了一个 getJspBody() 方法, 用于返回 JspFragment 对象。

3). JspFragment 的 invoke(Writer) 方法

把标签体内容从 Writer 中输出,若为 null,
则等同于 invoke(getJspContext().getOut()), 即直接把标签体内容输出到页面上。


有时, 可以 借助于 StringWriter, 可以在标签处理器类中先得到标签体的内容:

//1. 利用 StringWriter 得到标签体的内容.
StringWriter sw = new StringWriter();
bodyContent.invoke(sw);

//2. 把标签体的内容都变为大写
String content = sw.toString().toUpperCase();

4). 在 tld 文件中, 使用 body-content 节点来描述标签体的类型

<body-content>: 指定标签体的类型,大部分情况下, 取值为 scriptless。可能取值有 3 种:

  • empty: 没有标签体    
  • scriptless: 标签体可以包含 el 表达式和 JSP 动作元素,但不能包含 JSP 的脚本元素
  • tagdependent: 表示标签体交由标签本身去解析处理。若指定 tagdependent,在标签体中的所有代码都会原封不动的交给标签处理器,而不是将执行结果传递给标签处理器

<body-content>tagdependent</body-content>

5). 定义一个自定义标签

<mytag:printUpper time="10">abcdefg</mytag> 把标签体内容转换为大写, 并输出 time 次到浏览器上。

6). 实现 forEach 标签

    > 两个属性: items(集合类型, Collection),var(String 类型)

    > doTag:

        * 遍历 items 对应的集合
        * 把正在遍历的对象放入到 pageContext 中,键: var,值: 正在遍历的对象.
        * 把标签体的内容直接输出到页面上。

    <c:forEach items="${requestScope.customers }" var="cust2">
        ${pageScope.cust2.id } -- ${cust2.name } <br>
    </c:forEach>

 

2. 开发有父标签的标签

    @Override
    public void doTag() throws JspException, IOException {
        //1. 得到父标签的引用
        JspTag parent = getParent();
        
        //2. 获取父标签的 name 属性
        ParentTag parentTag = (ParentTag) parent;
        String name = parentTag.getName();
        
        //3. 把 name 值打印到 JSP 页面上.
        getJspContext().getOut().print("子标签输出name: " + name);
    }

 

1). 父标签无法获取子标签的引用

父标签仅把子标签作为标签体来使用

2). 子标签可以通过 getParent() 方法来获取父标签的引用(需继承 SimpleTagSupport 或自实现 SimpleTag 接口的该方法)
若子标签的确有父标签,JSP 引擎会把代表父标签的引用通过  setParent(JspTag parent)  赋给标签处理器

3). 父标签的类型是 JspTag 类型

该接口是一个空接口,但是来统一 SimpleTag 和 Tag 的。实际使用需要进行类型的强制转换。

4). 父标签的 <body-content></body-content>需设置为 scriptless

在 tld 配置文件中,无需为父标签有额外的配置。但,子标签是是以标签体的形式存在的,所以父标签的 <body-content></body-content>需设置为 scriptless。

5). 实现

<c:choose>
    <c:when test="${param.age > 24}">大学毕业</c:when>
    <c:when test="${param.age > 20}">高中毕业</c:when>
    <c:otherwise>高中以下...</c:otherwise>
</c:choose>

    > 开发 3 个标签: choose,when,otherwise
    > 其中 when 标签有一个 boolean 类型的属性: test
    > choose 是 when 和 otherwise 的父标签
    > when 在 otherwise 之前使用
    
    > 在父标签 choose 中定义一个 "全局" 的 boolean 类型的 flag:用于判断子标签在满足条件的情况下是否执行。
    
        * 若 when 的 test 为 true,且 when 的父标签的 flag 也为 true,则执行 when 的标签体(正常输出标签体的内容),
             同时把 flag 设置为 false
        * 若 when 的 test 为 true,且 when 的父标签的 flag 为 false,则不执行标签体。
        * 若 flag 为 true,otherwise 执行标签体。

以上是关于如何开发自定义标签的主要内容,如果未能解决你的问题,请参考以下文章

为片段制作自定义列表视图?

VSCode自定义代码片段——CSS选择器

如何在 Toad for Oracle 中使用自定义代码片段?

VSCode自定义代码片段——声明函数

VSCode 如何操作用户自定义代码片段(快捷键)

VSCode自定义代码片段6——CSS选择器