一步一步学JSP--JSTL(三)

1、JSTL介绍

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:指定标签库的路径。

2、jstl标签库

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>标签

<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>标签用于捕获嵌套在标签体中的内容抛出的异常,其语法格式如下:<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>标签

<c:if test=“”>标签可以构造简单的“if-then”结构的条件表达式。

--------------------c:if-------------------------<br/>
	<c:if test="${empty user }">
		对不起,您还未登录!
	</c:if>
  • <c: choose>标签

<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>标签

<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>标签

用来浏览一字符串中所有的成员,其成员是由定义符号所分隔的。

<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

-------------------------------------------------------------------------

  • <c:param>标签

在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>标签

用来引入包含文件的内容。

 --------------------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>标签

<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>标签用于实现请求重定向。

	 --------------------c:redirect-------------------------<br/>
	 <c:redirect url="/index.jsp" context="/el"></c:redirect>

3、自定义标签

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类:

  • 该类的实例对象代表 JSP 页面中的一段符合 JSP 语法规范的 JSP 片段,这段 JSP 片段不能包含 JSP 脚本元素(<% … %>)
  • JSP 引擎在处理简单标签的标签体时,会把标签体内容用一个 JspFragment  对象表示,并调用标签处理器对象的 setJspBody 方法把 JspFragment 对象传递给标签处理器对象。得到代表标签体的 JspFragment 对象后,标签开发者就可以在标签处理器中根据需要调用 JspFragment 对象的方法,进而决定如何处理标签体。
  • getJspContext 方法:该方法用于返回代表调用页面的 JspContext 对象

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>

关于简单标签的几点说明:  如果想要不执行标签下面的jsp代码,则只需在doTag方法中抛出javax.servlet.jsp.SkipPageException异常就行,简单标签处理类在每次调用标签时都会创建一次实例,所以是线程安全的。

3.3、经典标签

自定义标签的主要接口类:

  • TagSupport

使用最为频繁的类,可设置属性。

  • BodyTagSupport

可以处理body体部分的内容,使用不是很多。

  • SimpleTagSupport

简单标签实现类,是JSP2.0以后添加的新类,可以快速开发自定义标签。

Quick Start:

  • 用户需求:用户进入主页显示当前时间格式为:yyyy-MM-dd HH:mm:ss 星期一

按照JSP的开发原则,不建议在JSP页面上出现Java代码。而其他标签如JSTL又无法实现用户的需求,那就只能书写自定义的标签了。

开发:

  • 第一步:书写一个类实现Tag接口。

在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() {

	}
}
  • 第二步:在tld(Tag Lib Description)文件中描述标签接口的类文件。

可以通过MyEclipse的向导创建tld文件,建议使用tld1.2版本。也可以在jstl中获取一个tld文件然后加以修改。

	<tag>
 		<name>curDate</name>
 		<tag-class>cn.zq.tag.CurrentDateTag</tag-class>
 		<body-content>empty</body-content>
 	</tag>
  • 第三步:在web.xml中配置tld文件。(可选)在jsp-config中配置。
  • 第四步:在jsp页面上引用自定义标签。使用<%@ taglib …%>引用自定义标签。

    <%@ taglib uri="http://www.ricciozhang.cn/jsp/jstl/zq" prefix="zq" %>
    <zq:curDate/><br/>

       
API说明:

  • JspTag接口:

          所有的标签处理类,都要实现JspTag接口。这个接口只是一个标识接口,它里面没有定义任何方法。

  • Tag接口:

            Tag接口定义了所有传统标签处理类要实现的基本方法。

setPageContext(PageContext ctx)由Servlet容器调用。向当前的标签处理类传递当前PageContext对像。

setParent(Tag t) – 由Servlet容器调用此方法,向当前Tag对象传递父标签的Tag对像。

doStartTag() – 当遇到标签体开始标记时执行的方法,需要用户实现。

doEndTag() – 当遇到标签体结束标记时执行的方法。需要用户实现。

  doStartTag和doEndTag的返回值说明:

  • doStartTag和doEndTag方法都要求用户返回一个int类型的值,此值决定标签的后续部分如何处理。
  • doStartTag的两个返回值是:

Tag.SKIP_BODY:表示标签的主体部分将被忽略。

Tag.EVAL_BODY_INCLUDE:表示标签的主体部分将被正常执行。

  • doEndTag的两个返回值是:

Tag.SKIP_PAGE:表示停止标签后面的JSP代码的执行。

Tag.EVAL_PAGE:表示按正常顺序执行标签的后续JSP代码。

TagSupport类

  • TagSupport类添加了默认的实现,在实际的开发,基本上都是使用此类来开发自定义的标签。
  • 在使用此类进行开发时,只需要实现部分方法即可。
  • 此类支持自定义的属性。

再谈tld文件部署:

  • 第一种方式:

将tld文件放到WEB-INF/tlds目录下,项目可以自己识别此文件。即不需要在web.xml中配置即可以在jsp页面上引用。引用时使用在tld文件中定义的uri加以引用。

  • 第二种方式:

将你的标签处理类打包成jar文件,然后将tld文件放到jar包中的META-INF目录下,将此jar文件拷贝到lib目录下,即可以自动加载。

如果你是最终发布的应用程序建议使用第二种方式,

如果你是测试或开发阶段建议使用第一种方式。

小细节:

  • 当Servlet容器初始化自定义标签的处理类时,会调用它的无参数构造方法,所以,不要修改默认构造,更不要书写带有参数的构造方法。
  • 当继承TagSupport实现自定义的标签类时,不要在构造方法中访问pageContext变量。因为此时Servlet容器还没有调用setPageContext方法设置pageContext变量的值。
  • 当一个自定义标签初始化完成后,就由Servlet容器来进行管理。Servlet容器只会在第一次使用自定义标签时初始化自定义标签,且只会初始化一次。所以在Tomcat的生命周期内,自定义标签的构造方法只会执行一次。
  • 自定义标签的setPageContext方法在每次请求此标签时都会被执行一次。用于重新设置pageContext对像。
  • 如果是继承的TagSupport,则不建议覆盖setPageContext方法。
  • 如果实现的是Tag接口,则必须提供PageContext的成员变量。

BodyTagSupport类:

  • 如果希望操纵标签体的内容,可以让自定义处理类继承BodyTagSupport类。
  • BodyTagSupport类拥有一个bodyContent成员变量,用于缓存主体内容。
  • 当标签处理到标签体内容的时候,会调用setBodyContent方法将标签体内容保存到bodyContent变量当中。所以,不要试图在doStartTag方法中获取bodyContent的内容。因为尚没有读到body体部分。
  • 要处理body体部分,可以在doEndTag方法中进行处理。
  • 注意必须在doStartTag中设置返回值为BodyTag.EVAL_BODY_BUFFERED,Servlet容器才会将body部分通过setBodyContent方法设置到bodyContent变量上。对于其他的两个返回值:SKIP_BODY,EVAL_BODY_INCLUDE都不会调用setBodyContent方法。一定要加以注意。
  • bodyContent变量的getString()方法用于获取原始内容。
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标签:

  • JSP页面上通常在遍历集合中的数据。如之前大家使用过的c:forEach.
  • TagSupport类实现的IterationTag接口,所以使用此类完全可以完成遍历功能。
  • IterationTag接口扩展了一个返回值为:在doStartTag和doEndTag中都可以使用此值:

EVAL_BODY_AGIN可以用于重复执行body体部分。

  • IterationTag接口的doAfterBody方法中如果返回的是EVAL_BODY_AGIN则会重复执行body体部分。
  • doAfterBody方法会多次执行,如果要声明循环变量,则应该将循环变量声明成成员变量,在doAfterBody中进行迭代。

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>

用TagSupport实现遍历Collection集合的功能

  • TagSupport是IterationTag的子类:
  • 步骤如下:

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>

遍历Map类型的集合:

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 = {…};

保存基本数据的数组:int[] age={…}.

  • 对于对象数据:可以使用:

Object[] oo = (Object[])ss;

  • 但对于基本类型的数组:

Object[] oo = (Object[])age;//将抛出ClassCastException

  • 解决方案:使用Array对数组进行反射操作:

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);

}

4、自定义函数

自定义函数库:

  • 自定义函数与自定义标签一样也要配置到tld文件当中。
  • 自定义函数的方法必须声明成public static类型。
  • 以下实现一个自定义函数,它实现页面上两个数的相加。

第一步:写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") }

练习:实现一个sayHi自定义函数

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的相关内容

时间: 2024-11-09 10:00:54

一步一步学JSP--JSTL(三)的相关文章

一步一步跟我学DeviceOne开发 - 仿微信应用(一,二,三)

这是一个系列的文档,长期目标是利用DeviceOne开发一些目前使用广泛的优质手机应用,我们会最大化的实现这些应用的每一个功能和细节,不只停留在简单的UI模仿和Demo阶段,而是一个基本可以使用的实际App. 在实现的过程中,会有很多困难,还会发现有一些功能目前缺乏组件支持而无法实现,也会碰见各种移动开发中都会碰到的常见技术问题.一步一步的操作和问题的解决可以让开发者直观的了解通过DeviceOne如何开发一个实际App,也可以了解移动开发本身的很多技术细节,可以让App开发者少走很多弯路. 这

Rhythmk 一步一步学 JAVA (21) JAVA 多线程

1.JAVA多线程简单示例 1.1 .Thread  集成接口 Runnable 1.2 .线程状态,可以通过  Thread.getState()获取线程状态: New (新创建) Runnable (可以运行) Blocked  (被阻塞) Waiting  (等待) Timed waiting (计时等待) Terminated  (被终止) ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

一步一步学ROP之linux_x64篇

一步一步学ROP之linux_x64篇 一.序 **ROP的全称为Return-oriented programming(返回导向编程),这是一种高级的内存攻击技术可以用来绕过现代操作系统的各种通用防御(比如内存不可执行和代码签名等).上次我们主要讨论了linux_x86的ROP攻击:<一步一步学ROP之linux_x86篇>,在这次的教程中我们会带来上一篇的补充以及linux_x64方面的ROP利用方法,欢迎大家继续学习. 另外文中涉及代码可在我的github下载:https://githu

【DG】[三思笔记]一步一步学DataGuard

[DG][三思笔记]一步一步学DataGuard 它有无数个名字,有人叫它dg,有人叫它数据卫士,有人叫它data guard,在oracle的各项特性中它有着举足轻理的地位,它就是(掌声)......................Oracle Data Guard.而对于我而言,我一定要亲切的叫它:DG(注:主要是因为打着方便). 不少未实际接触过dg的初学者可能会下意识以为dg是一个备份恢复的工具.我要说的是,这种形容不完全错,dg拥有备份的功能,某些情况下它甚至可以与primary数据库

一步一步学ROP之linux_x86篇

0x00 本文仅解释说明蒸米大神一步一步学ROP之linux_x86篇,读者应先阅读这篇文章,遇到问题再来看我这篇文章. 阅读完这两篇文章后,我们会理解ROP(返回导向编程),DEP(堆栈不可执行),ASLR(内存地址随机化),Stack Protector(栈保护),Memory Leak. 0x01 第一个问题:为什么要构造成"A"*140+ret字符串,这个140是怎么来的呢? 要回答这个问题,我们需要把level1.c反汇编,level1.c代码如下: #include <

一步一步学Linq to sql系列文章 转lovecherry

http://www.cnblogs.com/lovecherry/archive/2007/08/13/853754.html 现在Linq to sql的资料还不是很多,本人水平有限,如果有错或者误导请指出,谢谢. 一步一步学Linq to sql(一):预备知识 一步一步学Linq to sql(二):DataContext与实体 一步一步学Linq to sql(三):增删改 一步一步学Linq to sql(四):查询句法 一步一步学Linq to sql(五):存储过程 一步一步学L

一步一步学ROP Linux x86

一步一步学ROP Linux x86 学习笔记一.无任何防护二.开启DEP三.开启DEP和ASLR四.开启DEP和ASLR,无libc 一步一步学ROP Linux x86 学习笔记 这部分文章主要依据的是蒸米大神的一步一步学ROP系列文章,我也是跟着做的,本文主要记录其中的问题和实验没有成功的地方. 一.无任何防护 在github可以找到相关的资料,不用自己编译漏洞代码了,也有写好的exp. 从最基础的开始,先学无任何防护的栈溢出.使用checksec看一下防护: 那就简单了,直接用shell

Rhythmk 一步一步学 JAVA (20) JAVA enum常用方法

JAVA 枚举定义常用方法: 1.static Enum valueOf(Class enum,String name) 返回指定name的枚举类型 2.Static Enum values[] 返回枚举常量集合 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50

一步一步学会puppet(三)--节点和模板

这篇主要介绍puppet中需要理解的2个重要概念:节点和模板: =================================================================== 1 节点 1.1 定义 1.2 详细说明 1.3 实例 2 模板 2.1 定义 2.2 详细说明 2.3 模板文件的语法 2.4 模板文件实例 2.5 使用模板文件生成实际配置文件 ===========================================================