JSP 自定义标签详解

简介: JSP 自定义标签详解

一、JSP自定义标签

1. 什么是标签

       标记语言,是一种注释文本的语言,以便于计算机可以操作。很多与“ML”结尾的语言都是标记语言,比如:HTML,XML,XHTML,VML等等。

标记语言与其他语言一样,也需要运行它们的环境,比如HTML的运行环境时浏览器,XML也要自己的解析和运行的环境。

2. 什么是自定义标签

       自定义标签是用户定义的JSP语言元素。当包含自定义标签的JSP页面转换为servlet时,这个标签就转换为一个名为tag handler的对象上的操作。之后当JSP页面的servlet执行时,Web容器就调用这些操作。

3. 标签语言的特点

       1.形式 :<开始标签 属性="属性值">标签体</结束标签>

      2.分类 :

空标签 br、hr...
ui标签 input、table...
控制标签 if、foreach...
数据标签 out

二、自定义标签的开发及使用步骤

       JSP自定义标签可以分为两种类型:标记库和函数库。

1. 标签库

       标记库是由一组标记组成的集合,这些标记可用于在JSP页面上执行各种操作。开发人员可以根据自己的需求创建自定义标记,并将其打包到一个标记库中。这个标记库可以在多个JSP页面中共享,并且可以轻松地部署和维护。

       简单来说是标记库一个JSP标签集合,它封装了JSP应用的通用核心功能, 基于JSP标签我们可以理解为,是JSP应该通用功能的一种封装方式。

2. 函数库

       函数库是一组函数的集合,这些函数可以在JSP页面上调用。开发人员可以根据自己的需求创建自定义函数,并将它们打包到一个函数库中。这个函数库可以在多个JSP页面中共享,并且可以轻松地部署和维护。

创建函数库需要以下步骤:

  • 创建一个名为“tld”的XML文件,并在其中定义函数库的属性,如名称、URI等。
  • 在JSP页面中引用这个XML文件,以便可以使用函数库中的函数。

创建函数

创建函数需要以下步骤:

  • 创建一个Java类,其中包含所需的函数。
  • 在tld文件中定义函数,并指定函数的名称、类、描述等信息。

3. 自定义标签使用步骤

3.1,先建一个 .tld 文件 (必须在) WEB-INF目录下

<!-- mytag.tld -->
<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_0.xsd"
  version="2.0">
  <description>My custom tag library</description>
  <display-name>My Tag Library</display-name>
  <tlib-version>1.0</tlib-version><!-- 代表标签库的版本号 -->
  <short-name>mtl</short-name><!-- 你的标签库简称 -->
    <uri>com.ycxw</uri><!-- taglib引入标签名字 -->
  <tag>
    <name>HelloTag</name><!-- 代表自定义标签的名字 -->
    <tag-class>com.ycxw.DemoTag</tag-class><!-- 对应的标签处理程序(助手类):包名+类名 -->
    <body-content>JSP</body-content><!-- 标签体内容的格式 -->
    <attribute>
      <name>test</name><!-- 自定义标签的属性名称 -->
      <required>true</required><!-- 该属性是否必填 -->
      <rtexprvalue>true</rtexprvalue><!-- 该属性值是否支持表达式 -->
    </attribute>
  </tag>
</taglib>

注:tld文件就是C标签的定义配置文件

  • 自定义标签是与 .tld文件相关的
  • 标签中的标签与 .tld中的tag元素相关,也就是tag元素对应的助手类有关

3.2 引用标签库

<%@taglib prefix="l" uri="com.ycxw" %>

3.3 每一个标签都必须有一个对于的助手类,继承BodyTagSupport

package com.ycxw;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;
/**
 * 
 * @author 云村小威
 *
 */
public class DemoTag extends BodyTagSupport{
  @Override
  public int doStartTag() throws JspException {
    System.out.println("—— doStartTag ——");
    return super.doStartTag();
  }
  @Override
  public int doAfterBody() throws JspException {
    System.out.println("—— doAfterBody ——");
    return super.doAfterBody();
  }
  @Override
  public int doEndTag() throws JspException {
    System.out.println("—— doEndTag ——");
    return super.doEndTag();
  }
}

3.4 测试

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!-- 引入标签库 -->
<%@taglib prefix="c" uri="com.ycxw" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>MyTag Text</title>
</head>
<body>
<c:HelloTag></c:HelloTag>
</body>
</html>

没有标签体内容运行结果:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!-- 引入标签库 -->
<%@taglib prefix="c" uri="com.ycxw" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>MyTag Text</title>
</head>
<body>
<c:HelloTag>hello</c:HelloTag>
</body>
</html>

有标签体内容运行结果:

下面将解释方法运行结果原因 —— 标签的生命周期

三、标签的生命周期

 1. JSP自定义标签生命周期图

       返回值作用:

SKIP_BODY 跳过主体
EVAL_BODY_INCLUDE 计算主体内容并输出
EVAL_BODY_AGAIN 再次计算主体一次
EVAL_PAGE 计算后续内容
SKIP_PAGE

跳过页面后续内容

2. 案例论证

       根据上一案例有标签体的情况下,默认会调用助手类doStartTag、doAfterBody、doEndTag方法。

1. 如果将doStartTag方法返回值改成SKIP_BODY,则doAfterBogy方法会跳过,不会运行。

@Override
  public int doStartTag() throws JspException {
    System.out.println("—— doStartTag ——");
    return SKIP_BODY;
  }

2. 如果将doStartTag方法返回值改成EVAL_BODY_INCLUDE,则doAfterBogy方法就会运行。

3. 如果将doAfterBody方法返回值修改成EVAL_BODY_AGAIN, 就会一直调用该方法进入死循环

4. 如果将doEndTag方法返回值修改成SKIP_PAGE, 则会跳过页面后的内容

       修改前:

修改后:

四、标签的开发实例

1. if 标签

1.1 进入.tld文夹定义if标签并对自定义标签进行描述

    <tag>
    <name>if</name>
    <tag-class>com.ycxw.Demo2Tag</tag-class>
    <body-content>JSP</body-content>
    <attribute>
      <name>text</name>
      <required>true</required>
      <rtexprvalue>true</rtexprvalue>
    </attribute>
  </tag>

1.2 编写助手类

package com.ycxw;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;
/**
 * if 控制标签
 * 
 * @author 云村小威
 *
 */
public class Demo2Tag extends BodyTagSupport{
  //接受自定义属性
  private boolean text;
  public boolean isText() {
    return text;
  }
  public void setText(boolean text) {
    this.text = text;
  }
  @Override
  public int doStartTag() throws JspException {
    //如果text为true就运行doaftertage,否则就跳过
    return text ? EVAL_BODY_INCLUDE : SKIP_BODY;
  }
}

1.3 编写jsp页面进行测试

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!-- 引入标签库 -->
<%@taglib prefix="w" uri="com.ycxw" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>MyTag Text</title>
</head>
<body>
<w:if text="true">小黑宝</w:if>
<w:if text="false">小黑子</w:if>
</body>
</html>

运行结果:

2. set和out标签

2.1 进入.tld文夹定义set和out标签并对自定义标签进行描述

<!-- settag -->
  <tag>
    <name>set</name>
    <tag-class>com.ycxw.SetTag</tag-class>
    <body-content>JSP</body-content>
    <attribute>
      <name>var</name>
      <required>true</required>
      <rtexprvalue>false</rtexprvalue>
    </attribute>
    <attribute>
      <name>value</name>
      <required>true</required>
      <rtexprvalue>true</rtexprvalue>
    </attribute>
  </tag>
  <!-- outtag -->
  <tag>
    <name>out</name>
    <tag-class>com.ycxw.OutTag</tag-class>
    <body-content>JSP</body-content>
    <attribute>
      <name>value</name>
      <required>true</required>
      <rtexprvalue>true</rtexprvalue>
    </attribute>
  </tag>

2.1 编写助手类

package com.ycxw;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;
/**
 * set 数据标签 
 * 作用:储存数据
 * 
 * @author 云村小威
 *
 */
public class SetTag extends BodyTagSupport {
  // 接受自定义属性
  private String var;
  private Object value;
  public String getVar() {
    return var;
  }
  public void setVar(String var) {
    this.var = var;
  }
  public Object getValue() {
    return value;
  }
  public void setValue(Object value) {
    this.value = value;
  }
  @Override
  public int doStartTag() throws JspException {
    // 要存储数据,需通过键值对方法进行储存,这里不考虑数据库只用pageContext作用域
    pageContext.setAttribute(var, value);
    return super.doStartTag();
  }
}
package com.ycxw;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyTagSupport;
/**
 * out 数据标签 
 * 作用:输出数据,首先要拿到输出流
 * 
 * @author 云村小威
 *
 */
public class OutTag extends BodyTagSupport {
  private Object value;
  public Object getValue() {
    return value;
  }
  public void setValue(Object value) {
    this.value = value;
  }
  @Override
  public int doStartTag() throws JspException {
    JspWriter out = pageContext.getOut();
    try {
      out.print(value);
    } catch (IOException e) {
      e.printStackTrace();
    }
    return super.doStartTag();
  }
}

2.3 编写jsp页面进行测试

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!-- 引入标签库 -->
<%@taglib prefix="w" uri="com.ycxw" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>MyTag Text</title>
</head>
<body>
<w:set var="name" value="ikun"></w:set>
<w:out value="${name }"></w:out>
</body>
</html>

运行结果:

3. foreach 标签

3.1 进入.tld文夹定义foreach标签并对自定义标签进行描述

<!-- foreach -->
  <tag>
    <name>foreach</name>
    <tag-class>com.ycxw.ForeachTag</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>

3.2 创建助手类

package com.ycxw;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;
/**
 * foreach 标签
 * 
 * @author 云村小威
 *
 */
public class ForeachTag extends BodyTagSupport {
  private String var;
  private List<Object> items = new ArrayList<Object>();
  public String getVar() {
    return var;
  }
  public void setVar(String var) {
    this.var = var;
  }
  public List<Object> getItems() {
    return items;
  }
  public void setItems(List<Object> items) {
    this.items = items;
  }
  @Override
  public int doStartTag() throws JspException {
    Iterator<Object> it = items.iterator();
    // 集合中没有元素会报错
    pageContext.setAttribute(var, it.next());
    pageContext.setAttribute("it", it);
    return EVAL_BODY_INCLUDE;
  }
  @Override
  public int doAfterBody() throws JspException {
    Iterator<Object> it = (Iterator<Object>) pageContext.getAttribute("it");
    //判断如果有元素就循环
    if (it.hasNext()) {
      pageContext.setAttribute(var, it.next());
      pageContext.setAttribute("it", it);
      return EVAL_BODY_AGAIN;
    } else {
      //没有就跳过
      return EVAL_PAGE;
    }
  }
}

实体类:

package com.ycxw;
/**
 * 实体类
 * 
 * @author 云村小威
 *
 */
public class Person {
  private int id;
  private String name;
  private int age;
  public int getId() {
    return id;
  }
  public void setId(int id) {
    this.id = id;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public int getAge() {
    return age;
  }
  public void setAge(int age) {
    this.age = age;
  }
  public Person(int id, String name, int age) {
    super();
    this.id = id;
    this.name = name;
    this.age = age;
  }
  @Override
  public String toString() {
    return "Person [id=" + id + ", name=" + name + ", age=" + age + "]";
  }
}

3.3 JSP 测试

<%@page import="java.util.ArrayList"%>
<%@page import="com.ycxw.Person"%>
<%@page import="java.util.List"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib uri="com.ycxw" prefix="w" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
  <% 
    List<Person> list = new ArrayList<Person>();
    list.add(new Person(1,"小黑宝",19));
    list.add(new Person(2,"纯路人",21));
    list.add(new Person(3,"ikun",27));
    request.setAttribute("list", list);
  %>
<w:foreach items="${list }" var="p">
  ${p }
</w:foreach>
</body>
</html>

运行结果:

4. select 标签

4.1 进入.tld文夹定义select标签并对自定义标签进行描述

    <tag>
        <name>select</name>
        <tag-class>com.ycxw.SelectTag</tag-class>
        <body-content>JSP</body-content>
        <attribute>
            <name>id</name>
            <required>false</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
        <!--数据源-->
        <attribute>
            <name>items</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
        <attribute>
            <name>textKey</name>
            <required>true</required>
            <rtexprvalue>false</rtexprvalue>
        </attribute>
        <attribute>
            <name>textVal</name>
            <required>true</required>
            <rtexprvalue>false</rtexprvalue>
        </attribute>
        <!--不是从数据库加载出来的数据,下拉框的头一个选项的value值-->
        <attribute>
            <name>headertextKey</name>
            <required>true</required>
            <rtexprvalue>false</rtexprvalue>
        </attribute>
        <!--不是从数据库加载出来的数据,下拉框的头一个选项的展示值-->
        <attribute>
            <name>headertextVal</name>
            <required>true</required>
            <rtexprvalue>false</rtexprvalue>
        </attribute>
        <attribute>
            <name>selectedVal</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
    </tag>

4.2 创建助手类

package com.ycxw;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.List;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyTagSupport;
import org.apache.commons.beanutils.PropertyUtils;
/**
 * select 标签 
 * 分析: 
 * 1.后台需要遍历->数据源 items 
 * 2.需要一个对象的属性代表下拉框对应的展示内容->textVal
 * 3.需要一个对象属性代表下拉框对应的value值 ->textKey 
 * 4.默认的头部选项展示内容 ->headerTextVal 
 * 5.默认的头部选项值->headerTextKey 
 * 6.数据中存储的值,为了方便做数据回显 -> selectedVal 
 * 7.方便获取获取标签设置样式...等等标记 id、name
 * 
 * @author 云村小威
 *
 */
public class SelectTag extends BodyTagSupport {
  private static final long serialVersionUID = 1L;
  private List<Object> items;
  private String textVal;
  private String textKey;
  private String headertextVal;
  private String headertextKey;
  private String selectedVal;
  private String id;
  public List<Object> getItems() {
    return items;
  }
  public void setItems(List<Object> items) {
    this.items = items;
  }
  public String getTextVal() {
    return textVal;
  }
  public void setTextVal(String textVal) {
    this.textVal = textVal;
  }
  public String getTextKey() {
    return textKey;
  }
  public void setTextKey(String textKey) {
    this.textKey = textKey;
  }
  public String getHeadertextVal() {
    return headertextVal;
  }
  public void setHeadertextVal(String headertextVal) {
    this.headertextVal = headertextVal;
  }
  public String getHeadertextKey() {
    return headertextKey;
  }
  public void setHeadertextKey(String headertextKey) {
    this.headertextKey = headertextKey;
  }
  public String getSelectedVal() {
    return selectedVal;
  }
  public void setSelectedVal(String selectedVal) {
    this.selectedVal = selectedVal;
  }
  public String getId() {
    return id;
  }
  public void setId(String id) {
    this.id = id;
  }
  @Override
  public int doStartTag() throws JspException {
    // 获取io流
    JspWriter out = pageContext.getOut();
    try {
      out.print(toHTML());
    } catch (IOException e) {
      e.printStackTrace();
    } catch (Exception e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    return super.doStartTag();
  }
  /**
   * 数据回显方法
   * @return
   * @throws Exception 
   */
  private String toHTML() throws Exception {
    // 利用stringbuffer拼接标签
    StringBuffer sb = new StringBuffer();
    sb.append("<select id='"+id+"'>");
    //判断不为空就给头部加默认选项
    if(headertextVal != null && !"".equals(headertextVal)) {
      sb.append("<option value='"+headertextKey+"'>"+headertextVal+"</option>");
    }
    //当集合有东西时才执行
    if (items.size() > 0) {
      for (Object obj : items) {
        // 利用反射获取到页面传过来的属性名对应的属性值(id)
        Field f = obj.getClass().getDeclaredField(textKey);
        //打开修饰符访问权限
        f.setAccessible(true);
        if(selectedVal != null && !"".equals(selectedVal) && selectedVal.equals(f.get(obj))) {
          sb.append("<option selected value='"+f.get(obj)+"'>"+PropertyUtils.getProperty(obj, textVal)+"</option>");
        }else {
          sb.append("<option value='"+f.get(obj)+"'>"+PropertyUtils.getProperty(obj, textVal)+"</option>");
        }
      }
    }
    sb.append("</select>");
    return sb.toString();
  }
}

4.3 JSP页面测试 :

<%@page import="java.util.ArrayList"%>
<%@page import="com.ycxw.Person"%>
<%@page import="java.util.List"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
  pageEncoding="UTF-8"%>
<%@taglib uri="com.ycxw" prefix="w"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
  <%
    List<Person> list = new ArrayList<Person>();
    list.add(new Person(1, "小黑宝", 19));
    list.add(new Person(2, "纯路人", 21));
    list.add(new Person(3, "ikun", 27));
    request.setAttribute("list", list);
  %>
  <w:select headertextVal="请选择" textVal="name" items="${list }"
    selectedVal="-1" headertextKey="-1" textKey="id"></w:select>
</body>
</html>

运行结果:


最后总结:开发自定义标签的目的就是给我们带来更方便的操作

1. 提高代码复用性

       自定义标签能够提高代码复用性,减少相似的代码出现在不同的页面中。

2. 更好的代码组织结构

       通过自定义标签,能够更好地组织代码结构,使代码更加清晰、易于维护、修改和调试。

3. 分离逻辑和展示层

       自定义标签能够将业务逻辑与展示层分离,增强了代码的可维护性和可读性。

4. 避免重复劳动

       对于那些需要频繁修改的页面元素,如导航栏、页脚等,自定义标签能够避免开发者不停地重复编写代码的工作。

      总之,自定义标签是JSP中非常有用的一个特性,它能够极大地提高代码的复用性、可维护性和可读性。

相关文章
|
Java
JSP自定义标签【下】
JSP自定义标签【下】
65 0
|
Java
jsp自定义标签
jsp自定义标签
75 1
|
5月前
|
Java 容器
JSP 教程 之 JSP 自定义标签 3
JSP自定义标签允许开发人员创建可重用的组件,简化页面逻辑。在JSP 2.0及以上版本,可通过继承`SimpleTagSupport`并覆写`doTag()`方法来创建自定义标签,如`HelloTag`,它有一个`message`属性。属性值通过setter方法`setMessage()`设置。在TLD文件中定义该属性后,可在JSP页面使用`&lt;ex:Hello message=&quot;This is custom tag&quot;/&gt;`来调用,输出定制的文本。
26 0
|
5月前
|
Java 容器
JSP 教程 之 JSP 自定义标签 4
**JSP自定义标签允许创建用户定义的语言元素。它们转换为Servlet中的tag handler,在执行时由Web容器调用。使用SimpleTagSupport继承并重写doTag()方法可创建简单标签。标签可设置属性,如message,通过setter方法访问。TLD文件定义属性元数据,JSP页面则通过prefix和uri引用。例如,&lt;ex:Hello message=&quot;...&quot;/&gt; 显示定制消息。属性可配置为必需、类型、描述及是否为JspFragment。**
29 0
|
5月前
|
搜索推荐 Java 容器
JSP 教程 之 JSP 自定义标签 2
**JSP自定义标签允许用户创建个性化标签,简化页面逻辑。在JSP 2.0中,通过继承`SimpleTagSupport`并重写`doTag()`可创建简单标签处理器。示例展示了一个名为`Hello`的自定义标签,它接收并显示标签体内容。TLD文件配置了标签元数据,JSP页面引用该标签并展示“这是消息主体”。**
24 0
|
5月前
|
Java 容器
JSP 教程 之 JSP 自定义标签 1
**JSP自定义标签简介**:扩展JSP功能,创建用户定义标签,通过Servlet容器调用Tag Handler。在JSP 2.0中,使用SimpleTagHandlers简化定制。以&quot;Hello&quot;标签为例,创建`HelloTag`类继承`SimpleTagSupport`,重写`doTag()`打印消息。编译后,在`custom.tld`定义标签库,JSP页面引用后即可使用。例如 `&lt;ex:Hello/&gt;` 显示 &quot;Hello Custom Tag!&quot;。
28 0
|
前端开发 Java
通用分页进阶之jsp之自定义标签
通用分页进阶之jsp之自定义标签
35 1
|
6月前
|
Java 数据安全/隐私保护
Jsp自定义标签(foreach,dept,select)
Jsp自定义标签(foreach,dept,select)
48 0
|
6月前
|
XML Java 数据格式
JSP自定义标签
JSP自定义标签
69 0
|
存储 Java 开发者
JSP自定义标签(下)
JSP自定义标签(下)
57 0