JSTL(JavaServer Pages Standard Tag Library)由JCP(Java Community Process)指定标准,提供给 Java Web 开发人员一个标准通用的标签函数库。和 EL 来取代传统直接在页面上嵌入 Java 程序(Scripting)的做法,以提高程序可读性、维护性和方便性。JSTL 主要由Apache组织的Jakarta Project 实现,容器必须支持Servlet 2.4 且JSP 2.0 以上版本。
JSTL下载地址:http://tomcat.apache.org/taglibs/standard/,最新版本为JSTL 1.2,本文下载的是JSTL1.1
下载下来的文件如下:
安装:
解压jakarta-taglibs-standard-1.1.2.zip,将解压后lib目录下的jstl.jar,standard.jar直接拷贝到工程下的WEB-INF/lib/目录下(如果用的是myeclipse可以不用复制这2个文件,myeclipse有自带的)。
导入标签库:
例如:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
- uri:用于导入标签库的uri。
- prefix:标签库的前缀,例如:<c:out>,c就是前缀,相当于为标签取个简单好记的名字。
- tagdir:指定标签库的路径。
jstl标签库包括以下几个部分:
- 核心标签库 (Core tag library)
- 国际化标签 (I18N—capable formatting tag library)
- 数据库标签(SQL tag library)
- XML标签(XML tag library)
- JSTL函数标签(Functions tag library)--EL函数
2.1、核心标签库
- <c:out>标签
用于输出数据的内容,一般可以用脚本表达式来输出数据,<c:out>标签的功能更强大。
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> --------------------c:out-------------------------<br/> <%--直接输出字符串内容--%> "string" = <c:out value="string"></c:out><br/> <% //put name into requestScope. pageContext.setAttribute("name", "RiccioZhang", PageContext.REQUEST_SCOPE); %> <%--直接取值并输出--%> requestScope.name = <c:out value="${name }" ></c:out><br/> <%--${xxx }是取不到值的,直接用default属性的值输出--%> \${xxx } = <c:out value="${xxx }" default="defaultValue"></c:out><br/> <%-- 与上面是等价的:<c:out value="${xxx }" default="defaultValue">, defaultValue属性和在标签体中写默认值只能选择其一,否则会抛出异常 --%> \${xxx } = <c:out value="${xxx }">defaultValue</c:out><br/> <%-- 注意:如果value="",那么就会直接输出空串,而不会输出默认值。 --%> blankString = <c:out value="" default="defaultValue"></c:out><br/> <% //put paragraph into requestScope. pageContext.setAttribute("p", "<p style='color: red'>This is a paragraph.</p>", PageContext.REQUEST_SCOPE); %> <%--对字符进行转义--%> <c:out value="${requestScope.p }"></c:out><br/> <c:out value="${requestScope.p }" escapeXml="true"></c:out> <c:out value="${requestScope.p }" escapeXml="false"></c:out><br/>运行结果如下:-------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------
- <c:set >标签
- <c:set>标签用来将变量存储到jsp范围中或者javabean中。
- 格式1:<c:set value=“value” var=“varName” [scope=“page|request|”] />
- 格式2:<c:set target=“varName” property=“name” [scope=“session|application”] />
Book.java
package cn.zq.domain; public class Book { private String id; private String title; private Double price; public Book() { } public Book(String id, String title, Double price) { this.id = id; this.title = title; this.price = price; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public Double getPrice() { return price; } public void setPrice(Double price) { this.price = price; } public String toString(){ return this.getClass().getSimpleName() + "[id = "+id+", title="+title+", price="+price+"]"; } }
--------------------c:set-------------------------<br/> 将变量存储到jsp范围中:<br/> <%-- value属性中的值等价于在标签体中写的值(可以为el表达式),但是两者不能同时存在。 --%> <c:set var="name" value="RiccioZhang" scope="page"></c:set> 1--> pageScope.name = <c:out value="${name }" /><br/> <c:set var="name2" scope="session">RiccioZhang2</c:set> 2--> sessionScope.name2 = <c:out value="${name2 }" /><br/> 设置javabean的属性:<br/> <% Book book = new Book("001", "Thinking in java", 66.8); pageContext.setAttribute("book", book, PageContext.REQUEST_SCOPE); %> book = <c:out value="${book }"></c:out><br/> <c:set target="${book }" property="id" value="002" ></c:set> <c:set target="${book }" property="title" value="Java Core I"></c:set> <c:set target="${book }" property="price" value="88.8"></c:set> <%--注意当没有var属性时指定scope属性会抛出异常--%> book = <c:out value="${book }"></c:out><br/> <c:set target="${book }" property="id" var="b" scope="session">003</c:set> <c:set target="${book }" property="title">Java Core II</c:set> <c:set target="${book }" property="price" value="102"></c:set> book = <c:out value="${book }"></c:out><br/> b = <c:out value="${sessionScope.b }"></c:out><br/> 设置Map:<br/> <% Map<String, String> map = new HashMap<String, String>(); pageContext.setAttribute("map", map, PageContext.REQUEST_SCOPE); %> <c:set target="${requestScope.map }" property="name" value="RiccioZhang"></c:set> <c:set target="${requestScope.map }" property="age" value="22"></c:set> <c:set target="${requestScope.map }" property="address" value="GZ"></c:set> \${requestScope.map } = ${requestScope.map }
运行结果如下:
---------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------
<c: remove>标签用于移除各种web域的属性。<c:remove var="varName" [scope="{page|request|session|application}"] />
--------------------c:remove-------------------------<br/> <% Object o = new Object(); pageContext.setAttribute("o", o, PageContext.PAGE_SCOPE); pageContext.setAttribute("o", o, PageContext.REQUEST_SCOPE); pageContext.setAttribute("o", o, PageContext.SESSION_SCOPE); pageContext.setAttribute("o", o, PageContext.APPLICATION_SCOPE); %> <%-- 未指定scope属性,则会调用pageContext.removeAttribute(name)方法, 会从各个域中移除 --%> <c:remove var="o"/> \${pageScope.o} = ${pageScope.o}<br/> \${requestScope.o} = ${requestScope.o}<br/> \${sessionScope.o} = ${sessionScope.o}<br/> \${applicationScope.o} = ${applicationScope.o}<br/> <% o = new Object(); pageContext.setAttribute("o", o, PageContext.PAGE_SCOPE); pageContext.setAttribute("o", o, PageContext.REQUEST_SCOPE); pageContext.setAttribute("o", o, PageContext.SESSION_SCOPE); pageContext.setAttribute("o", o, PageContext.APPLICATION_SCOPE); %> <%-- 指定scope,则从指定域中移除 --%> <c:remove var="o" scope="page"/> <c:remove var="o" scope="application"/> \${pageScope.o} = ${pageScope.o}<br/> \${requestScope.o} = ${requestScope.o}<br/> \${sessionScope.o} = ${sessionScope.o}<br/> \${applicationScope.o} = ${applicationScope.o}<br/>
执行结果如下:---------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------
<c:catch>标签用于捕获嵌套在标签体中的内容抛出的异常,其语法格式如下:<c:catch [var="varName"]>nested actions</c:catch>。var属性用于标识<c:catch>标签捕获的异常对象,它将保存在page这个Web域中。
--------------------c:catch-------------------------<br/> <c:catch var="ex"> <% int i = 1/0; %> </c:catch> \${pageScope.ex.message } = ${pageScope.ex.message }<br/> \${ex.cause } = ${ex.cause }<br/> \${ex.stackTrace } = ${ex.stackTrace}<br/>
结果如下:---------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------
<c:if test=“”>标签可以构造简单的“if-then”结构的条件表达式。
--------------------c:if-------------------------<br/> <c:if test="${empty user }"> 对不起,您还未登录! </c:if>
<c:choose>标签用于指定多个条件选择的组合边界,它必须与<c:when>和<c:otherwise>标签一起使用。使用<c:choose>,<c:when>和<c:otherwise>三个标签,可以构造类似 “if-else if-else” 的复杂条件判断结构。
--------------------c:choose-------------------------<br/> <c:set var="num" value="${param.num }"></c:set> <c:choose> <c:when test="${empty num}"> 您未输入任何数据! </c:when> <c:otherwise> 您输入的是:${num } </c:otherwise> </c:choose>
请求url:http://localhost:8080/jstl/jstl.jsp?num=10086,结果如下:
---------------------------------------------------------------------------------------------------------
--------------------c:choose-------------------------
您输入的是:10086
---------------------------------------------------------------------------------------------------------
<c:forEach>标签用于对一个集合对象中的元素进行循环迭代操作,或者按指定的次数重复迭代执行标签体中的内容。
语法1:
<c:forEach [var="varName"]
items="collection" [varStatus="varStatusName"]
[begin="begin"]
[end="end"]
[step="step"]>
//Body内容
</c:forEach>语法2:
<c:forEach [var="varName"]
[varStatus="varStatusName"]
begin="begin"
end="end"
[step="step"]>
//Body内容
</c:forEach>
--------------------c:forEach-------------------------<br/> <% Map<String, String> person = new HashMap<String, String>(); person.put("name", "RiccioZhang"); person.put("age", "22"); person.put("addr", "GZ"); pageContext.setAttribute("person", person); %> 不使用标签迭代map:<br/> <% for(Map.Entry<String, String> entry : person.entrySet()){ pageContext.setAttribute("entry", entry); %> ${entry.key } = ${entry.value }<br/> <% } %> 标签迭代--> Map:<br/> <%-- varStatus保存着当前的迭代信息, 他有如下几个属性: * index 现在迭代成员的索引 * count 成员的总数 * first 当前是否为第一个成员 * last 当前是否为最后一个成员 可以用begin指定迭代从哪个索引开始, end指定从哪个索引处结束(包含), step指定迭代的步长。 --%> <c:forEach var="en" items="${person }" varStatus="stat"> \${stat.index} = ${stat.index}<br/> \${stat.count} = ${stat.count}<br/> \${stat.first} = ${stat.first}<br/> \${stat.last} = ${stat.last}<br/> ${en.key } = ${en.value }<br/> -------------------------------------------------<br/> </c:forEach> 标签迭代--> Collection:<br/> <% Collection<Book> books = new HashSet<Book>(); books.add(new Book("001", "Thinking in java", 78.0)); books.add(new Book("002", "Java Core I", 67.0)); books.add(new Book("003", "Java Core II", 102.0)); pageContext.setAttribute("books", books); %> <c:forEach var="book" items="${books }"> \${book} = ${book}<br/> </c:forEach> 标签迭代--> array:<br/> <% pageContext.setAttribute("colors", new String[]{"red", "blue", "green", "pink", "dark"}); %> <c:forEach var="color" items="${colors }"> \${color } = ${color }<br/> </c:forEach> <%-- 需求: 迭代Collection中的Map中的数组中的Collection. --%> <% Collection<Map<String, Collection[]>> coll = new ArrayList<Map<String, Collection[]>>(); Map<String, Collection[]> m = new HashMap<String, Collection[]>(); for(int i = 1; i <= 3; i++){ String key = "s" + i; Collection<String>[] arrays = new Collection[i]; for(int j = 0; j < arrays.length; j++){ Collection<String> c = new ArrayList<String>(); c.add("a("+i+", "+j+")"); arrays[j] = c; } m.put(key, arrays); } coll.add(m); pageContext.setAttribute("coll", coll); %> <c:forEach var="m" items="${coll}"> <c:forEach var="entry" items="${m }"> ${entry.key } = [ <c:forEach var="arr" items="${entry.value }" > <c:forEach var="hs" items="${arr }"> <c:forEach var="s" items="${hs}"> ${s } </c:forEach> </c:forEach> </c:forEach> ] <br/> </c:forEach> </c:forEach> <c:forEach var="floor" begin="1" end="10" step="1"> 第${floor }楼<br/> </c:forEach>
运行结果如下:*******************************************************
--------------------c:forEach-------------------------
不使用标签迭代map:
age = 22
name = RiccioZhang
addr = GZ
标签迭代--> Map:
${stat.index} = 0
${stat.count} = 1
${stat.first} = true
${stat.last} = false
age = 22
-------------------------------------------------
${stat.index} = 1
${stat.count} = 2
${stat.first} = false
${stat.last} = false
name = RiccioZhang
-------------------------------------------------
${stat.index} = 2
${stat.count} = 3
${stat.first} = false
${stat.last} = true
addr = GZ
-------------------------------------------------
标签迭代--> Collection:
${book} = Book[id = 001, title=Thinking in java, price=78.0]
${book} = Book[id = 002, title=Java Core I, price=67.0]
${book} = Book[id = 003, title=Java Core II, price=102.0]
标签迭代--> array:
${color } = red
${color } = blue
${color } = green
${color } = pink
${color } = dark
s2 = [ a(2 0) a(2 1) ]
s1 = [ a(1 0) ]
s3 = [ a(3 0) a(3 1) a(3 2) ]
第1楼
第2楼
第3楼
第4楼
第5楼
第6楼
第7楼
第8楼
第9楼
第10楼
*******************************************************
用来浏览一字符串中所有的成员,其成员是由定义符号所分隔的。
<c:forTokens
items="stringOfTokens"
delims="delimiters"
[var="varName"]
[varStatus="varStatusName"]
[begin="begin"]
[end="end"]
[step="step"]>
//body内容
</c:forTokens>
--------------------c:forTokens-------------------------<br/> <c:forTokens var="d" items="aa,bb,cc" delims=","> \${d} = ${d }<br/> </c:forTokens> <% pageContext.setAttribute("o", new Object()); %> <c:forTokens var="d2" items="${o }" delims="@"> \${d2} = ${d2 }<br/> </c:forTokens>
-------------------------------------------------------------------------
--------------------c:forTokens-------------------------
${d} = aa
${d} = bb
${d} = cc
${d2} = java.lang.Object
${d2} = 1eb01e5
-------------------------------------------------------------------------
在JSP页面进行URL的相关操作时,经常要在URL地址后面附加一些参数。<c:param>标签可以嵌套在<c:import>、<c:url>或<c:redirect>标签内,为这些标签所使用的URL地址附加参数。
<c:param>标签在为一个URL地址附加参数时,将自动对参数值进行URL编码,例如,如果传递的参数值为“中国”,则将其转换为“%d6%d0%b9%fa”后再附加到URL地址后面,这也就是使用<c:param>标签的最大好处。
示例:<c:param name="name" value="value" />
用来引入包含文件的内容。
--------------------c:import-------------------------<br/> 直接通过var获取:<br/> <c:import url="/1.txt" charEncoding="GBK"> </c:import> <br/> 通过varReader获取:<br/> <c:import url="/1.txt" varReader="r" charEncoding="GBK"> <% StringReader reader = (StringReader)pageContext.findAttribute("r"); char[] buf = new char[1024]; int len = -1; while( ( len = reader.read(buf) ) != -1 ){ out.print(new String(buf, 0, len)); } %> </c:import>
<c:url>标签用于在JSP页面中构造一个URL地址,其主要目的是实现URL重写。URL重写就是将会话标识号以参数形式附加在URL地址后面
--------------------c:url-------------------------<br/> <c:url var="url" value="/aaa/xxx.jsp" scope="page"> <c:param name="country" value="中国"></c:param> <c:param name="city" value="武汉"></c:param> </c:url> \${url } = ${url }<br/>
结果如下:${url } = /day11/aaa/xxx.jsp?country=%e4%b8%ad%e5%9b%bd&city=%e6%ad%a6%e6%b1%89
<c:redirect>标签用于实现请求重定向。
--------------------c:redirect-------------------------<br/> <c:redirect url="/index.jsp" context="/el"></c:redirect>
3.1、什么是自定义标签
自定义标签是指JSP自定义标签。自定义标签在功能上逻辑上与JavaBean类似,都封装Java代码。自定义标签是可重用的组件代码,并且允许开发人员为复杂的操作提供逻辑名称。自定义标签是JSP1.1规范里最早提出的。从标签的来源上看,JSP的标签库可以分为两种类型:一种是JSP标准标签库(JSTL,JSP Standard Tag Library),它是JSP开发环境供应商开发的。另一种是JSP开发环境的使用者(即用户)自己定义的标签。通过使用标签库,让JSP页面变得更加简洁,减少了JSP页面脚本代码量,大大降低JSP页面的复杂度,并且是代码最大程度是重用。
标签的几种形式:
- 主体和内容都为空的:<demo:hello />
- 包含属性的标签:<demo:hello name=“zq”/>
- 包含主体内容的标签:<demo:hello>RiccioZhang</demo:hello>
- 包含主体内容和属性的标签:<demo:hello name=“zq”>RiccioZhang</demo:hello>
- 嵌套的标签:
<demo:hello>
<demo:user name=“zq”/
</demo:hello>标签库的接口和类的继承关系(标签库的api定义在javax.servlet.jsp.tagext下):
开发自定义标签,其核心就是要编写处理器类,一个标签对应一个标签处理器类,而一个标签库则是很多标签处理器的集合。JSP所有的标签处理器类都实现javax.servlet.jsp.tagext.JspTag接口。这个接口是一个标记接口 。它有两个直接子接口:
- 简单标签:标签处理类实现SimpleTag接口,它是JSP2.0新增加的接口,代表简单的标签。
- 经典标签:JSP2.0以前标签处理类实现Tag接口,它是经典的必须实现的接口,它有一个直接子接口IterationTag
自定义标签执行流程:
开发一个自定义的标签包含以下步骤:
1、根据业务要求确定标签形式
2、创建自定义标签的处理类。
3、创建自定义标签的库描述文件*.tld(Tag Library Descriptor File)
4、将tld描述文件放到WEB-INF或其子目录下。
5、在web.xml中声明所引用的自定义标签(在servlet2.4,jsp2.0以上的版本不用配置此项)。
6、在页面上使用JSP的自定义标签。3.2、简单标签
为了简化自定义标签的开发,JSP2.0开始又引入了一种新的标签扩展机制。
称为“简单标签扩展”:
1、对于熟悉Java编程语言的开发人员,可以定义实现javax.servlet.jsp.tagext.SimpleTag接口的标签处理类。
2、SimpleTag的一个子类是:SimpleTagSupport。SimpleTag接口的优点:
- 和JSP1.2中的已有接口不同的是,SimpleTag接口不使用doStartTag()和doEndTag()方法,而提供一个简单的doTag()方法。这个方法在调用该标记时只被使用一次,需要在一个自定义标记中实现所有逻辑过程、循环和对标记体的输出等。从这个方面来讲,SimpleTag可以和IterationTag达到同等的作用。但SimpleTag的方法和周期要简单的多。
- 在SimpleTag中还有用来设置JSP内容的setJspBody()和getJspBody()方法。web容器使用setJspBody()方法定义一个代表JSP内容的JspFragment对象。实现SimpleTag标记的程序可以在doTage方法中根据需要多次调用getJspBody().invokie()方法以处理JSP内容
- 对于前台web页面制作人员,在JSP1.2时代Taglib页面调用实际上是比较复杂的,SimpleTag+EL表达式语言极大的简化了taglib调用,对不懂java的人员也可以轻松编写JSP页面的目的。
实现SimpleTag接口:
- setJspContext方法:该方法把代表JSP页面的pageContext对象传递给标签处理器对象
- setParent方法:该方法把父标签处理器对象传递给当前标签处理器对象
- getParent()方法:该方法用于获得标签的父标签处理器对象
- setJspBody:方法:由Servlet容器调用此方法,用于设置标签的主体部分。
- JspFrgment类代表JSP的部分代码,它的方法有:invoke方法用于执行body体部分的内容。将内容输出到IE上。
- doTag() 方法:负责标签的处理过程,完成所有的标签逻辑。与doStartTag和doEndTag方法不同的是doTag方法没有返回值。该方法可以抛出javax.servlet.jsp.SkipPageException 异常,用于通知web容器不在执行JSP页面中位于结束标记后面的内容。
- SimpleTag接口的一个实现类为SimpleTagSupport。开发时,只需要继承SimpleTagSupport类,并覆盖doTag方法即可。
实现SimpleTag接口的标签处理器的生命周期:
- 自定义一个标签输出系统当前时间
(1)写一个java类
package cn.zq.tag; import java.io.IOException; import java.io.Writer; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import javax.servlet.jsp.JspContext; import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.JspFragment; import javax.servlet.jsp.tagext.JspTag; import javax.servlet.jsp.tagext.SimpleTag; public class MyDateTag implements SimpleTag{ private JspContext context; //接收pageContext对象 /* * 将pageContext对象传递进来 */ public void setJspContext(JspContext context) { this.context = context; } public void doTag() throws JspException, IOException { Writer writer = context.getOut(); DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SS"); writer.write(df.format(new Date())); } public JspTag getParent() { return null; } public void setJspBody(JspFragment fragment) { } public void setParent(JspTag tag) { } }
(2) 在WEB-INF目录下新建一个tld目录,并在这个目录下新建一个my.tld(其实就是一个xml格式的文件)文件(解析引擎会在WEB-INF以及子目录下查找tld文件):my.tld
<?xml version="1.0" encoding="UTF-8" ?> <taglib xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd" version="2.1"> <description>my taglib</description> <display-name>my taglib</display-name> <tlib-version>1.0</tlib-version> <short-name>my</short-name> <uri>http://www.ricciozhang.cn/jsp/jstl/my</uri> <tag> <name>date</name> <tag-class>cn.zq.tag.MyDateTag</tag-class> <!-- empty 标签体为空 --> <body-content>empty</body-content> </tag> </taglib>(3) 配置tld文件(可选)在web.xml文件中可以做如下的配置:
<jsp-config> <taglib> <taglib-uri>http://www.ricciozhang.cn/jsp/jstl/my</taglib-uri> <taglib-location>/WEB-INF/tld/my.tld</taglib-location> </taglib> </jsp-config>(4)使用标签
引入标签库:<%@ taglib uri="http://www.ricciozhang.cn/jsp/jstl/my" prefix="my" %>
使用:<my:date/>
说明:
- 此标签类也必须要配置到tld文件中。
- 对于SimpleTagSupport配置文件中的<body-content>子元素,由于其主体不能包含Java程序片段,即它不处理body部分的内容,所以<body-content>子元素的值不能为JSP.
- 如果需要使用session等信息,可以先通过获取pageContext的方式获取:PageContext pc = (PageContext)getJspContext();
body-content元素说明:
Body-content元素可以包含的值为:
- JSP:接收所有的JSP语法。
- EMPTY:没有标签体
- tagdependent:不解析body部分的标签。由自定义标签接收数据再行处理。若指定该值,标签体中的所有代码原封不动的交给标签处理器,而不是将执行结果传递给标签处理器
- scriptless:接收EL,文本和JSP动作,但不接收<%= ..%>java脚本片段。
如:如果使用scriptless则使用以下语法是错误的:行(2)
1 <my:simple name=“zq">
2 <%="Hello"%>
3 <br/>
</my:simple>
如果将第二行换成:<c:out value=‘Hello’/>就可以了
(1)java类
package cn.zq.tag; import java.io.IOException; import java.io.Writer; import javax.servlet.jsp.JspContext; import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.JspFragment; import javax.servlet.jsp.tagext.JspTag; import javax.servlet.jsp.tagext.SimpleTag; public class MyMaxValueTag implements SimpleTag{ private JspContext context; private Integer num1; private Integer num2; public void setNum1(Integer num1) { this.num1 = num1; } public void setNum2(Integer num2) { this.num2 = num2; } public void setJspContext(JspContext pc) { context = pc; } public void doTag() throws JspException, IOException { Writer out = context.getOut(); if(num1 > num2){ out.write("最大值为:" + num1); }else{ out.write("最大值为:" + num2); } } public JspTag getParent() { return null; } public void setJspBody(JspFragment jspBody) { } public void setParent(JspTag parent) { } }
(2)配置tld文件
<?xml version="1.0" encoding="UTF-8" ?> <taglib xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd" version="2.1"> <tlib-version>1.0</tlib-version> <short-name>my2</short-name> <uri>http://www.ricciozhang.cn/jsp/jstl/my2</uri> <tag> <name>max</name> <tag-class>cn.zq.tag.MyMaxValueTag</tag-class> <body-content>empty</body-content> <attribute> <name>num1</name> <required>true</required> <!-- 是否支持el表达式 --> <rtexprvalue>true</rtexprvalue> <type>java.lang.Integer</type> </attribute> <attribute> <name>num2</name> <required>true</required> <!-- 是否支持el表达式 --> <rtexprvalue>true</rtexprvalue> <type>java.lang.Integer</type> </attribute> </tag> </taglib>
(3)使用标签
<my2:max num2="12" num1="14"/>
package cn.zq.tag; import java.io.IOException; import java.io.Writer; import javax.servlet.jsp.JspContext; import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.JspFragment; import javax.servlet.jsp.tagext.JspTag; import javax.servlet.jsp.tagext.SimpleTag; public class CircleTag implements SimpleTag{ private JspContext context; private Integer begin; private Integer end; private Integer step; public void setBegin(Integer begin) { this.begin = begin; } public void setContext(JspContext context) { this.context = context; } public void setEnd(Integer end) { this.end = end; } public void setStep(Integer step) { this.step = step; } public void setJspContext(JspContext pc) { context = pc; } public void doTag() throws JspException, IOException { Writer out = context.getOut(); for(int i = begin; i <= end; i+=step){ out.write( i +"<br/>"); } } public JspTag getParent() { return null; } public void setJspBody(JspFragment jspBody) { } public void setParent(JspTag parent) { } }
<?xml version="1.0" encoding="UTF-8" ?> <taglib xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd" version="2.1"> <tlib-version>1.0</tlib-version> <short-name>my2</short-name> <uri>http://www.ricciozhang.cn/jsp/jstl/my3</uri> <tag> <name>circle</name> <tag-class>cn.zq.tag.CircleTag</tag-class> <body-content>empty</body-content> <attribute> <name>step</name> <required>true</required> <!-- 是否支持el表达式 --> <rtexprvalue>true</rtexprvalue> <type>java.lang.Integer</type> </attribute> <attribute> <name>end</name> <required>true</required> <!-- 是否支持el表达式 --> <rtexprvalue>true</rtexprvalue> <type>java.lang.Integer</type> </attribute> <attribute> <name>begin</name> <required>true</required> <!-- 是否支持el表达式 --> <rtexprvalue>true</rtexprvalue> <type>java.lang.Integer</type> </attribute> </tag> </taglib>
使用:
<%@ taglib uri="http://www.ricciozhang.cn/jsp/jstl/my3" prefix="my3" %>
<my3:circle begin="1" end="10" step="1"/>
package cn.zq.tag; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; import javax.servlet.jsp.JspContext; import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.JspFragment; import javax.servlet.jsp.tagext.JspTag; import javax.servlet.jsp.tagext.SimpleTag; public class MyToUpperCaseTag implements SimpleTag{ private JspContext context; private JspFragment jspBody; private JspTag parent; public void setJspContext(JspContext pc) { context = pc; } public void doTag() throws JspException, IOException { Writer out = context.getOut(); StringWriter sw = new StringWriter(); jspBody.invoke(sw); out.write(sw.toString().toUpperCase()); } public JspTag getParent() { return parent; } public void setJspBody(JspFragment jspBody) { this.jspBody = jspBody; } public void setParent(JspTag parent) { this.parent = parent; } }
<?xml version="1.0" encoding="UTF-8" ?> <taglib xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd" version="2.1"> <tlib-version>1.0</tlib-version> <short-name>my4</short-name> <uri>http://www.ricciozhang.cn/jsp/jstl/my4</uri> <tag> <name>toUpperCase</name> <tag-class>cn.zq.tag.MyToUpperCaseTag</tag-class> <body-content>scriptless</body-content> </tag> </taglib>
<my4:toUpperCase> asdfGDDDeyehehe </my4:toUpperCase>
JspFragment类:
Invoke 方法(java.io.Writer out):该方法用于执行 JspFragment 对象所代表的 JSP 代码片段。在 doTag() 方法中可以根据需要调用该方法。
该方法的参数 out 用于指定将 JspFragment 对象的执行结果写入到哪个输出流对象中。若传递参数 out 的值为 null,则将执行结果写入到 JspContext.geOut() 方法返回的输出流对象中。
若想在标签处理器中修改标签体内容:需在调用 invoke 方法时指定一个可取出结果数据的输出流对象(如:StringWriter),让标签体的执行结果输出到该输出流中,然后从该输出流对象中取出数据进行修改后再输出到目标设备
使用SimpleTagSupport实现遍历集合:
package cn.zq.tag; import java.io.IOException; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.servlet.jsp.JspContext; import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.JspFragment; import javax.servlet.jsp.tagext.SimpleTagSupport; public class ForEachTag extends SimpleTagSupport{ @SuppressWarnings("rawtypes") private Iterator iterator; private String var; public void setItems(Object items) { //if items is null, then return empty iterator. if(items == null){ iterator = Collections.EMPTY_LIST.iterator(); }else{ if(items instanceof Collection){ Collection coll = (Collection) items; iterator = coll.iterator(); }else if(items instanceof Map){ Map map = (Map) items; iterator = map.entrySet().iterator(); }else if(items.getClass().isArray()){ List list = new ArrayList(); for(int i = 0; i < Array.getLength(items); i++){ Object item = Array.get(items, i); list.add(item); } iterator = list.iterator(); }else{ throw new IllegalArgumentException("Can't iterate " + items.getClass().getName()); } } } public void setVar(String var) { this.var = var; } public void doTag() throws JspException, IOException { if("".equals(var)){ throw new NullPointerException("var can't be blank."); } JspContext context = getJspContext(); JspFragment body = getJspBody(); while(iterator.hasNext()){ Object o = iterator.next(); context.setAttribute(var, o); body.invoke(null); } context.removeAttribute(var); } }
<tag> <name>forEach</name> <tag-class>cn.zq.tag.ForEachTag</tag-class> <body-content>scriptless</body-content> <attribute> <name>items</name> <required>true</required> <rtexprvalue>true</rtexprvalue> <type>java.lang.Object</type> </attribute> <attribute> <name>var</name> <required>true</required> <rtexprvalue>false</rtexprvalue> <type>java.lang.String</type> </attribute> </tag>
<% pageContext.setAttribute("arrs", new int[]{1,2,3,4,5,6,7}); %> <zq:forEach items="${ arrs}" var="arr"> ${arr}<br/> </zq:forEach> <% pageContext.setAttribute("list", Arrays.asList(new String[]{"zq", "zy", "lzh"})); %> <zq:forEach items="${ list}" var="item"> ${item}<br/> </zq:forEach> <% Map map = new HashMap(); for(int i = 0; i < 4; i++){ Book b = new Book(String.valueOf(i), "title"+i, i*10 + 2.5); map.put(b.getId(), b); } pageContext.setAttribute("map", map); %> <zq:forEach items="${map }" var="entry"> ${entry.key } = ${entry.value }<br/> </zq:forEach>
自定义标签的主要接口类:
使用最为频繁的类,可设置属性。
可以处理body体部分的内容,使用不是很多。
简单标签实现类,是JSP2.0以后添加的新类,可以快速开发自定义标签。
Quick Start:
按照JSP的开发原则,不建议在JSP页面上出现Java代码。而其他标签如JSTL又无法实现用户的需求,那就只能书写自定义的标签了。
开发:
在doStartTag中书写要输出的代码。
package cn.zq.tag; import java.io.IOException; import java.io.Writer; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import javax.servlet.jsp.JspException; import javax.servlet.jsp.PageContext; import javax.servlet.jsp.tagext.Tag; public class CurrentDateTag implements Tag{ private PageContext pc; private Tag parent; public CurrentDateTag(){ System.out.println(this.getClass().getName()); } /** * 1.设置pageContext对象 */ public void setPageContext(PageContext pc) { this.pc = pc; } /** * 2.设置父标签 */ public void setParent(Tag t) { parent = t; } /** * 3.处理开始标签 */ public int doStartTag() throws JspException { Writer out = pc.getOut(); DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss E"); try { out.write(df.format(new Date())); } catch (IOException e) { throw new RuntimeException(e); } return 0; } /** * 4、处理结束标签 */ public int doEndTag() throws JspException { return 0; } public Tag getParent() { return parent; } public void release() { } }
可以通过MyEclipse的向导创建tld文件,建议使用tld1.2版本。也可以在jstl中获取一个tld文件然后加以修改。
<tag> <name>curDate</name> <tag-class>cn.zq.tag.CurrentDateTag</tag-class> <body-content>empty</body-content> </tag>
<%@ taglib uri="http://www.ricciozhang.cn/jsp/jstl/zq" prefix="zq" %> <zq:curDate/><br/>
API说明:
所有的标签处理类,都要实现JspTag接口。这个接口只是一个标识接口,它里面没有定义任何方法。
Tag接口定义了所有传统标签处理类要实现的基本方法。
setPageContext(PageContext ctx)由Servlet容器调用。向当前的标签处理类传递当前PageContext对像。
setParent(Tag t) – 由Servlet容器调用此方法,向当前Tag对象传递父标签的Tag对像。
doStartTag() – 当遇到标签体开始标记时执行的方法,需要用户实现。
doEndTag() – 当遇到标签体结束标记时执行的方法。需要用户实现。
doStartTag和doEndTag的返回值说明:
Tag.SKIP_BODY:表示标签的主体部分将被忽略。
Tag.EVAL_BODY_INCLUDE:表示标签的主体部分将被正常执行。
Tag.SKIP_PAGE:表示停止标签后面的JSP代码的执行。
Tag.EVAL_PAGE:表示按正常顺序执行标签的后续JSP代码。
TagSupport类
再谈tld文件部署:
将tld文件放到WEB-INF/tlds目录下,项目可以自己识别此文件。即不需要在web.xml中配置即可以在jsp页面上引用。引用时使用在tld文件中定义的uri加以引用。
将你的标签处理类打包成jar文件,然后将tld文件放到jar包中的META-INF目录下,将此jar文件拷贝到lib目录下,即可以自动加载。
如果你是最终发布的应用程序建议使用第二种方式,
如果你是测试或开发阶段建议使用第一种方式。
小细节:
BodyTagSupport类:
package cn.zq.tag; import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspWriter; import javax.servlet.jsp.tagext.BodyTagSupport; public class BodyDemoTag extends BodyTagSupport{ private static final long serialVersionUID = -7320348789459562269L; public int doStartTag() throws JspException { return EVAL_BODY_BUFFERED; } public int doEndTag() throws JspException { try { String str = getBodyContent().getString(); JspWriter out = pageContext.getOut(); out.print(str); } catch (Exception e) { e.printStackTrace(); } return EVAL_PAGE; } }
<tag> <name>BodyDemo</name> <tag-class>cn.zq.tag.BodyDemoTag</tag-class> <body-content>JSP</body-content> </tag>
<% pageContext.setAttribute("name", "RiccoZhang"); %> <zq:BodyDemo> name = ${name }<br/> </zq:BodyDemo>
创建和使用Iterator标签:
EVAL_BODY_AGIN可以用于重复执行body体部分。
Iterator简单实例:输出Body部分N次
package cn.zq.tag; import javax.servlet.jsp.JspException; import javax.servlet.jsp.PageContext; import javax.servlet.jsp.tagext.IterationTag; import javax.servlet.jsp.tagext.Tag; public class SimpleIteratorTag implements IterationTag{ private Tag parent; private PageContext pc; private int i = 0; public void setPageContext(PageContext pc) { i = 0; this.pc = pc; } public void setParent(Tag t) { parent = t; } public int doStartTag() throws JspException { return EVAL_BODY_INCLUDE; } public int doAfterBody() throws JspException { if(i < 10){ i++; System.err.println("i >> " + i); return EVAL_BODY_AGAIN; } return SKIP_BODY; } public int doEndTag() throws JspException { return EVAL_PAGE; } public Tag getParent() { return parent; } public void release() { System.out.println("release...."); } }
<tag> <name>simpleIterator</name> <tag-class>cn.zq.tag.SimpleIteratorTag</tag-class> <body-content>scriptless</body-content> </tag>
1、TagSupport实现了IterationTag接口,并添加了默认实现。
2、通过属性接收需要遍历的集合,同时要声明一个变量。以便于将信息保存到pageContext中。
package cn.zq.tag; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.TagSupport; public class IteratorCollectionTag extends TagSupport{ private static final long serialVersionUID = 2606376711004300944L; private Iterator items; //集合公用迭代器 private String var; public void setVar(String var) { this.var = var; } public void setItems(Collection items){ if(items != null){ this.items = items.iterator(); }else{ this.items = Collections.EMPTY_LIST.iterator(); } } public int doStartTag() throws JspException { return EVAL_BODY_INCLUDE; } public int doAfterBody() throws JspException { if(items.hasNext()){ pageContext.setAttribute(var, items.next()); return EVAL_BODY_AGAIN; } return SKIP_BODY; } }
<tag> <name>iteratorCollection</name> <tag-class>cn.zq.tag.IteratorCollectionTag</tag-class> <body-content>JSP</body-content> <attribute> <name>items</name> <required>true</required> <rtexprvalue>true</rtexprvalue> <type>java.util.Collection</type> </attribute> <attribute> <name>var</name> <required>true</required> <rtexprvalue>false</rtexprvalue> <type>java.lang.String</type> </attribute> </tag>
package cn.zq.tag; import java.util.Collections; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.TagSupport; public class IteratorMapTag extends TagSupport{ private static final long serialVersionUID = 3276279245657573036L; private String var; private Iterator<Map.Entry> items; public void setVar(String var) { this.var = var; } public void setItems(Map map){ if(map == null){ items = Collections.EMPTY_MAP.entrySet().iterator(); }else{ items = map.entrySet().iterator(); } } public int doStartTag() throws JspException { return EVAL_BODY_INCLUDE; } public int doAfterBody() throws JspException { if(items.hasNext()){ pageContext.setAttribute(var, items.next()); return EVAL_BODY_AGAIN; } return SKIP_BODY; } }
<tag> <name>iteratorMap</name> <tag-class>cn.zq.tag.IteratorMapTag</tag-class> <body-content>JSP</body-content> <attribute> <name>items</name> <required>true</required> <rtexprvalue>true</rtexprvalue> <type>java.util.Map</type> </attribute> <attribute> <name>var</name> <required>true</required> <rtexprvalue>false</rtexprvalue> <type>java.lang.String</type> </attribute> </tag> </taglib>
package cn.zq.tag; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.TagSupport; public class IteratorTag extends TagSupport{ private static final long serialVersionUID = -3164171313267237222L; private Iterator<? extends Object> items; //集合通用接口 private String var; public void setVar(String var){ this.var = var; } public void setItems(Object o){ if(o == null){ items = new ArrayList().iterator(); }else if(o instanceof Collection){ Collection coll = (Collection) o; items = coll.iterator(); }else if(o instanceof Map){ Map map = (Map) o; items = map.entrySet().iterator(); }else if(o.getClass().isArray()){ int len = Array.getLength(o); List list = new ArrayList(); for(int i = 0; i < len; i++){ Object ob = Array.get(o, i); list.add(ob); } items = list.iterator(); }else{ throw new IllegalArgumentException("items can't be iterted."); } } public int doStartTag() throws JspException { return EVAL_BODY_INCLUDE; } public int doAfterBody() throws JspException { if(items.hasNext()){ pageContext.setAttribute(var, items.next()); return EVAL_BODY_AGAIN; } pageContext.removeAttribute(var); return SKIP_BODY; } }
<tag> <name>iterator</name> <tag-class>cn.zq.tag.IteratorTag</tag-class> <body-content>JSP</body-content> <attribute> <name>items</name> <required>true</required> <rtexprvalue>true</rtexprvalue> <type>java.lang.Object</type> </attribute> <attribute> <name>var</name> <required>true</required> <rtexprvalue>false</rtexprvalue> <type>java.lang.String</type> </attribute> </tag>
----------iterator---------<br/> <zq:iterator items="${arrs }" var="arr"> ${arr }<br/> </zq:iterator> <zq:iterator items="${list }" var="l"> ${l }<br/> </zq:iterator> <zq:iterator items="${map }" var="entry"> ${entry.key } = ${entry.value }<br/> </zq:iterator>
对象数组:String[] ss = {…};
Object[] oo = (Object[])ss;
Object[] oo = (Object[])age;//将抛出ClassCastException
int len = Array.getLength(age);
List<Object> list = new ArrayList<Object>();
for(int i=0;i<len;i++){
Object oo = Array.get(age,i);
list.add(oo);
}
自定义函数库:
第一步:写java代码
package cn.zq.fun; public class AddFun { public static int add(String x, String y){ int c = new Integer(x) + new Integer(y); return c; } }
第二步:建立一个taglib2.0以上版本的tld文件。
第三步:在tld文件中添加以下配置。
<?xml version="1.0" encoding="UTF-8"?> <taglib xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd" version="2.1"> <tlib-version>1.0</tlib-version> <short-name>fn</short-name> <uri>http://www.zq.cn/jsp/jstl/fn</uri> <function> <name>add</name> <function-class>cn.zq.fun.AddFun</function-class> <function-signature>int add(java.lang.String, java.lang.String)</function-signature> </function> </taglib>
<%@ taglib uri="http://www.zq.cn/jsp/jstl/fn" prefix="fn" %> ${fn:add("1","2") }
package cn.zq.fun; public class SayHiFuc { public static String sayHi(String name){ return "Hi, " + name; } }
<function> <name>sayHi</name> <function-class>cn.zq.fun.SayHiFuc</function-class> <function-signature>java.lang.String sayHi(java.lang.String)</function-signature> </function>${fn:sayHi("RiccioZhang") }
关于标准函数库的相关内容,请查看fn.tld的相关内容
原文地址:http://blog.csdn.net/ricciozhang/article/details/43372779