目录
3.1 先建一个 .tld 文件 (必须在) 在WEB-INF目录下
3.3 每一个标签都必须有一个对于的助手类,继承BodyTagSupport
一、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中非常有用的一个特性,它能够极大地提高代码的复用性、可维护性和可读性。