JSP 自定义标签详解

目录

一、JSP自定义标签

1. 什么是标签

2. 什么是自定义标签

3. 标签语言的特点

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

1. 标签库

2. 函数库

3. 自定义标签使用步骤

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

3.2 引用标签库

扫描二维码关注公众号,回复: 15849259 查看本文章

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

3.4 测试

三、标签的生命周期

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

2. 案例论证

四、标签的开发实例

1. if 标签

2. set和out标签

3. foreach 标签

4. select 标签


一、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中非常有用的一个特性,它能够极大地提高代码的复用性、可维护性和可读性。

猜你喜欢

转载自blog.csdn.net/Justw320/article/details/131367352
今日推荐