1.什么是自定义标签?
用户定义的一种自定义的jsp标记 。当一个含有自定义标签的jsp页面被jsp引擎编译成servlet时,tag标签被转化成了对一个称为 标签处理类 的对象的操作。于是,当jsp页面被jsp引擎转化为servlet后,实际上tag标签被转化为了对tag处理类的操作。
2.导入JSTL标签库:
通过JSTL标签遍历集合元素
<%@page import="com.zhaoliang.tag.Customer"%> <%@page import="java.util.List"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!-- 导入JSTL标签库,注意:如果没有 /jsp 会出现 XXX does not support runtime expressions--> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!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=ISO-8859-1"> <title>Insert title here</title> </head> <body> <!-- 遍历customers中元素,并打印name和age --> <c:forEach items="${requestScope.customers }" var="customer"> --${customer.name },${customer.age }<br> </c:forEach> </body> </html>
<%@page import="com.zhaoliang.tag.Customer"%> <%@page import="java.util.ArrayList"%> <%@page import="java.util.List"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> <!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=ISO-8859-1"> <title>Insert title here</title> </head> <body> <% //模拟Servlet中的操作 List<Customer> customers = new ArrayList<Customer>(); customers.add(new Customer("AA",11)); customers.add(new Customer("BB",12)); customers.add(new Customer("CC",13)); request.setAttribute("customers", customers); %> <jsp:forward page="testtag.jsp"></jsp:forward> </body> </html>
3.标签库介绍:
1)接口和类之间的关系
2)自定义标签分类:
(1)空标签:<hello/>
(2)带有属性的空标签:
<max num1=“3” num2=“5”/>
(3)带有内容的标签:
<greeting>
hello
</greeting>
(4)带有内容和属性的标签:
<greeting name=“Tom”>
hello
</greeting>
3)自定义标签
(1)开发自定义标签,其核心就是要编写处理器类,一个标签对应一个标签处理器类,而一个标签库则是很多标签处理器的集合。所有的标签处理器类都要实现 JspTag 接口,该接口中没有定义任何方法,主要作为 Tag 和 SimpleTag 接口的父接口。
(2)开发自定义标签的步骤:①编写完成标签功能的 Java 类(标签处理器) ②编写标签库描述(tld)文件,在tld文件中对自定义中进行描述 ③在 JSP 页面中导入和使用自定义标签(3)标签类中方法介绍
4)自定义空标签:<hello/>
(1)编写标签处理类(HelloSimpleTag):该类实现了 SimpleTag 接口
import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.jsp.JspContext; import javax.servlet.jsp.JspException; import javax.servlet.jsp.PageContext; import javax.servlet.jsp.tagext.JspFragment; import javax.servlet.jsp.tagext.JspTag; import javax.servlet.jsp.tagext.SimpleTag; public class HelloSimpleTag implements SimpleTag { <span style="white-space:pre"> </span>@Override <span style="white-space:pre"> </span>public void doTag() throws JspException, IOException { <span style="white-space:pre"> </span>pageContext.getOut().print("hello"); <span style="white-space:pre"> </span>HttpServletRequest request = (HttpServletRequest)pageContext.getRequest(); <span style="white-space:pre"> </span>pageContext.getOut().print("hello : " + request.getParameter("name")); <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>@Override <span style="white-space:pre"> </span>public JspTag getParent() { <span style="white-space:pre"> </span>// TODO Auto-generated method stub <span style="white-space:pre"> </span>return null; <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>@Override <span style="white-space:pre"> </span>public void setJspBody(JspFragment arg0) { <span style="white-space:pre"> </span>// TODO Auto-generated method stub <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span> <span style="white-space:pre"> </span>private PageContext pageContext; <span style="white-space:pre"> </span>@Override <span style="white-space:pre"> </span>public void setJspContext(JspContext arg0) { <span style="white-space:pre"> </span>this.pageContext = (PageContext)arg0; <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>@Override <span style="white-space:pre"> </span>public void setParent(JspTag arg0) { <span style="white-space:pre"> </span>// TODO Auto-generated method stub <span style="white-space:pre"> </span>} }(2)编写标签库描述文件(*.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>tagZ</short-name> <!-- 作为TLD 文件的ID,用来唯一标识当前的 TLD 文件,多个TLD 文件的 URI 不能重复, 通过JSP页面的 taglib 标签的 uri 属性来引用 --> <uri>http://www.haha.com/mytag/core</uri> <!-- 描述自定义的 HelloSimpleTag 标签 --> <tag> <!-- 标签的名称 --> <name>hello</name> <!-- 标签对应类的全类名 --> <tag-class>com.haha.tag.HelloSimpleTag</tag-class> <!-- 标签体类型 --> <body-content>empty</body-content> </tag> </taglib>(3)在JSP 页面导入标签库
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <!-- 导入标签库(描述文件) --> <%@ taglib prefix="tagz" uri="http://www.zhaoliang.com/mytag/core" %> <!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=ISO-8859-1"> <title>Insert title here</title> </head> <body> <tagz:hello/> </body> </html>
(1)在标签处理类中定义 XXX 属性和 setXXX() 方法
import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.jsp.JspContext; import javax.servlet.jsp.JspException; import javax.servlet.jsp.PageContext; import javax.servlet.jsp.tagext.JspFragment; import javax.servlet.jsp.tagext.JspTag; import javax.servlet.jsp.tagext.SimpleTag; public class HelloSimpleTag implements SimpleTag { private String value; private int count; public void setValue(String value) { this.value = value; } public void setCount(int count) { this.count = count; } @Override public void doTag() throws JspException, IOException { pageContext.getOut().print(count + " : " + value); pageContext.getOut().print("<br>"); HttpServletRequest request = (HttpServletRequest)pageContext.getRequest(); pageContext.getOut().print("hello : " + request.getParameter("name")); } @Override public JspTag getParent() { // TODO Auto-generated method stub return null; } @Override public void setJspBody(JspFragment arg0) { // TODO Auto-generated method stub } private PageContext pageContext; @Override public void setJspContext(JspContext arg0) { this.pageContext = (PageContext)arg0; } @Override public void setParent(JspTag arg0) { // TODO Auto-generated method stub } }(2)在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>tagZ</short-name> <!-- 作为TLD 文件的ID,用来唯一标识当前的 TLD 文件,多个TLD 文件的 URI 不能重复, 通过JSP页面的 taglib 标签的 uri 属性来引用 --> <uri>http://www.zhaoliang.com/mytag/core</uri> <!-- 描述自定义的 HelloSimpleTag 标签 --> <tag> <!-- 标签的名称 --> <name>hello</name> <!-- 标签对应类的全类名 --> <tag-class>com.zhaoliang.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>true</rtexprvalue> </attribute> </tag> </taglib>(3)使用标签
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <!-- 导入标签库(描述文件) --> <%@ taglib prefix="tagz" uri="http://www.zhaoliang.com/mytag/core" %> <!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=ISO-8859-1"> <title>Insert title here</title> </head> <body> <tagz:hello value="${param.name }" count="${param.count }" /> </body> </html>6)带有内容的标签:
(1)若标签中含有标签体,则在标签处理器中使用 JspFragment 对象封装标签体信息
(2)若标签含有标签体,则 JSP 引擎会调用 setJspBody() 方法把 JspFragment 对象传给标签处理器。在SimpleTagSupport 中还定义了一个 getJspBody() 方法用于返回 JspFragment 对象
(3)JspFragment 的 invoke(Writer) 方法:把标签体内容从Writer中输出,若为null,则等同于invoke(getJspContext().getOut()),即直接把标签体内容输出到页面上。
(4)在TLD 文件中,使用<body-content>来描述标签体类型,取值:
①empty:没有标签体
②scriptless:标签体可以包含 el 表达式和 JSP 动作元素,但不能包含 JSP 的脚本元素
③tagdependent:表示标签体交由标签本身去解析处理。若指定 tagdependent,在标签体中的所有代码都会原封不动的交给标签处理器,而不是将执行结果传递给标签处理器
练习:自定义带有属性和标签体的标签:将标签体的内容转换为大写,并输出time次
处理器类(TestFragment)
import java.io.IOException; import java.io.StringWriter; import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.JspFragment; import javax.servlet.jsp.tagext.SimpleTagSupport; public class TestFragment extends SimpleTagSupport { private int time; public void setTime(int time) { this.time = time; } @Override public void doTag() throws JspException, IOException { //1.得到标签体内容 JspFragment bodycontent = getJspBody(); StringWriter sw = new StringWriter(); bodycontent.invoke(sw); String content = sw.toString(); //2.将标签体内容转换成大写 content.toUpperCase(); //3.循环输出 for(int i = 0;i < time;i++){ getJspContext().getOut().print(content); getJspContext().getOut().print("<br>"); } } }配置TLD
<tag> <name>toUperCase</name> <tag-class>com.javalearning.tag.TestFragment</tag-class> <body-content>scriptless</body-content> <attribute> <name>time</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag>测试
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="tag" uri="http://www.java.com/mytag_1/core"%> <!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=ISO-8859-1"> <title>Insert title here</title> </head> <body> <tag:toUperCase time="5">zhaoliang</tag:toUperCase> </body> </html>
练习:模拟forEach标签
编写标签处理器
import java.io.IOException; import java.util.List; import javax.servlet.jsp.JspException; import javax.servlet.jsp.PageContext; import javax.servlet.jsp.tagext.SimpleTagSupport; import org.apache.catalina.connector.Request; public class ListIteratorWithBody extends SimpleTagSupport { private List lists; private String var; public void setLists(List lists) { this.lists = lists; } public void setVar(String var) { this.var = var; } @Override public void doTag() throws JspException, IOException { if(lists != null){ for(Object obj : lists){ getJspContext().setAttribute(var, obj); getJspBody().invoke(null); } } } }配置 TLD 文件
<tag> <name>forEach</name> <tag-class>com.javalearning.tag.ListIteratorWithBody</tag-class> <body-content>scriptless</body-content> <attribute> <name>lists</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <name>var</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag>测试
<% List<Customer> lists = new ArrayList<Customer>(); lists.add(new Customer("AA",12)); lists.add(new Customer("BB",13)); lists.add(new Customer("CC",14)); request.setAttribute("lists", lists); %> <tag:forEach lists="${requestScope.lists }" var="cust"> ${cust.name }--${cust.age } </tag:forEach>
7)带有父标签的标签:
<greeting name=“Tom”>(1)父标签无法获取子标签的引用,父标签仅把子标签当作标签体来使用
(2)子标签可以通过 getParent() 方法来引用父标签(需要继承 SimpleTagSupport 类或者实现 SimpleTag 接口的该方法):若子标签的确有父标签,JSP 会把带有父标签的引用通过 setParent(JspTag parent) 来赋给标签处理器
(3)由于父标签类型为 JspTag 类型,该接口是一个空接口,用来统一 SimpleTag 和 Tag 的,实际使用需要进行类型的强制转换。
(4)在TLD 文件中,不需要为父标签进行额外的配置,但子标签是以标签体存在的,所以父标签的:<body-content>scriptless</body-content>
<%-- 1.开发3个标签:choose when otherwise 2.其中when标签有一个boolean类型的属性:test 3.choose是when和otherwise的父标签,when在otherwise之前使用 4.在父标签choose中定义一个“全局”的 boolean 类型变量 flag : 用于判断子标签在满足条件的情况下是否执行 1)若 when 的 test 为 true,且 when 父标签的 flag 也为 true,则执行 when 的标签体,并将 flag 置为 false 2)若 when 的 test 为 true,且 when 父标签的 flag 为 false,则不执行 when 的标签体 3)若 flag 为 true,otherwise执行标签体 --%> <tag:choose> <tag:when test="${param.age > 22}">大学毕业</tag:when> <tag:when test="${param.age > 18}">高中毕业</tag:when> <tag:otherwise>高中以下...</tag:otherwise> </tag:choose>
(1)编写choose标签处理器
import java.io.IOException; import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.SimpleTagSupport; public class ChooseTag extends SimpleTagSupport { private boolean flag = true; public void setFlag(boolean flag) { this.flag = flag; } public boolean isFlag() { return flag; } @Override public void doTag() throws JspException, IOException { getJspBody().invoke(null); } }(2)编写when标签处理器
import java.io.IOException; import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.SimpleTagSupport; public class WhenTag extends SimpleTagSupport { private boolean test; public void setTest(boolean test) { this.test = test; } @Override public void doTag() throws JspException, IOException { if(test){ ChooseTag chooseTag = (ChooseTag)getParent(); boolean flag = chooseTag.isFlag(); if(flag){ getJspBody().invoke(null); chooseTag.setFlag(false); } } } }(3)编写otherwise标签处理器
import java.io.IOException; import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.SimpleTagSupport; public class OtherwiseTag extends SimpleTagSupport { @Override public void doTag() throws JspException, IOException { ChooseTag chooseTag = (ChooseTag)getParent(); if(chooseTag.isFlag()){ getJspBody().invoke(null); } } }(4)配置标签
<tag> <name>choose</name> <tag-class>com.javalearning.tag.ChooseTag</tag-class> <body-content>scriptless</body-content> </tag> <tag> <name>when</name> <tag-class>com.javalearning.tag.WhenTag</tag-class> <body-content>scriptless</body-content> <attribute> <name>test</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag> <tag> <name>otherwise</name> <tag-class>com.javalearning.tag.OtherwiseTag</tag-class> <body-content>scriptless</body-content> </tag>(5)测试
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="tag" uri="http://www.java.com/mytag_1/core" %> <!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> <%-- 1.开发3个标签:choose when otherwise 2.其中when标签有一个boolean类型的属性:test 3.choose是when和otherwise的父标签,when在otherwise之前使用 4.在父标签choose中定义一个“全局”的 boolean 类型变量 flag : 用于判断子标签在满足条件的情况下是否执行 1)若 when 的 test 为 true,且 when 父标签的 flag 也为 true,则执行 when 的标签体,并将 flag 置为 false 2)若 when 的 test 为 true,且 when 父标签的 flag 为 false,则不执行 when 的标签体 3)若 flag 为 true,otherwise执行标签体 --%> <tag:choose> <tag:when test="${param.age > 22}">大学毕业</tag:when> <tag:when test="${param.age > 18}">高中毕业</tag:when> <tag:otherwise>高中以下...</tag:otherwise> </tag:choose> </body> </html>