一步一步学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

一步一步学JSP--JSTL(三)_第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就是前缀,相当于为标签取个简单好记的名字。
  • tagdir:指定标签库的路径。

2、jstl标签库

jstl标签库包括以下几个部分:

  • 核心标签库 (Core tag library)
  • 国际化标签 (I18N—capable formatting tag library)
  • 数据库标签(SQL tag library)
  • XML标签(XML tag library)
  • JSTL函数标签(Functions tag library)--EL函数

一步一步学JSP--JSTL(三)_第2张图片

2.1、核心标签库

  • 标签

用于输出数据的内容,一般可以用脚本表达式来输出数据,标签的功能更强大。

一步一步学JSP--JSTL(三)_第3张图片

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
--------------------c:out-------------------------
<%--直接输出字符串内容--%> "string" =
<% //put name into requestScope. pageContext.setAttribute("name", "RiccioZhang", PageContext.REQUEST_SCOPE); %> <%--直接取值并输出--%> requestScope.name =
<%--${xxx }是取不到值的,直接用default属性的值输出--%> \${xxx } =
<%-- 与上面是等价的:, defaultValue属性和在标签体中写默认值只能选择其一,否则会抛出异常 --%> \${xxx } = defaultValue
<%-- 注意:如果value="",那么就会直接输出空串,而不会输出默认值。 --%> blankString =
<% //put paragraph into requestScope. pageContext.setAttribute("p", "

This is a paragraph.

", PageContext.REQUEST_SCOPE); %> <%--对字符进行转义--%>

运行结果如下:

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


一步一步学JSP--JSTL(三)_第4张图片


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

  • 标签
  • 标签用来将变量存储到jsp范围中或者javabean中。
  • 格式1:
  • 格式2:

       一步一步学JSP--JSTL(三)_第5张图片

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-------------------------
将变量存储到jsp范围中:
<%-- value属性中的值等价于在标签体中写的值(可以为el表达式),但是两者不能同时存在。 --%> 1--> pageScope.name =
RiccioZhang2 2--> sessionScope.name2 =
设置javabean的属性:
<% Book book = new Book("001", "Thinking in java", 66.8); pageContext.setAttribute("book", book, PageContext.REQUEST_SCOPE); %> book =
<%--注意当没有var属性时指定scope属性会抛出异常--%> book =
003 Java Core II book =
b =
设置Map:
<% Map map = new HashMap(); pageContext.setAttribute("map", map, PageContext.REQUEST_SCOPE); %> \${requestScope.map } = ${requestScope.map }

                运行结果如下:

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


一步一步学JSP--JSTL(三)_第6张图片


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

  • 标签

标签用于移除各种web域的属性。

	--------------------c:remove-------------------------
<% 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)方法, 会从各个域中移除 --%> \${pageScope.o} = ${pageScope.o}
\${requestScope.o} = ${requestScope.o}
\${sessionScope.o} = ${sessionScope.o}
\${applicationScope.o} = ${applicationScope.o}
<% 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,则从指定域中移除 --%> \${pageScope.o} = ${pageScope.o}
\${requestScope.o} = ${requestScope.o}
\${sessionScope.o} = ${sessionScope.o}
\${applicationScope.o} = ${applicationScope.o}

执行结果如下:

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


一步一步学JSP--JSTL(三)_第7张图片

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

  • 标签

         标签用于捕获嵌套在标签体中的内容抛出的异常,其语法格式如下:nested actions。var属性用于标识标签捕获的异常对象,它将保存在page这个Web域中。

--------------------c:catch-------------------------
<% int i = 1/0; %> \${pageScope.ex.message } = ${pageScope.ex.message }
\${ex.cause } = ${ex.cause }
\${ex.stackTrace } = ${ex.stackTrace}

结果如下:

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


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

  • 标签
标签可以构造简单的“if-then”结构的条件表达式

一步一步学JSP--JSTL(三)_第8张图片

--------------------c:if-------------------------
对不起,您还未登录!
  • 标签
标签用于指定多个条件选择的组合边界,它必须与标签一起使用。使用三个标签,可以构造类似 “if-else if-else” 的复杂条件判断结构。
--------------------c:choose-------------------------
您未输入任何数据! 您输入的是:${num }

请求url:http://localhost:8080/jstl/jstl.jsp?num=10086,结果如下:

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

--------------------c:choose-------------------------
您输入的是:10086

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

  • 标签

标签用于对一个集合对象中的元素进行循环迭代操作,或者按指定的次数重复迭代执行标签体中的内容。

语法1:
    items="collection" [varStatus="varStatusName"]
    [begin="begin"]
    [end="end"]
    [step="step"]>
        //Body内容

语法2:
    [varStatus="varStatusName"]
    begin="begin"
    end="end"
    [step="step"]>
        //Body内容

一步一步学JSP--JSTL(三)_第9张图片

	--------------------c:forEach-------------------------
<% Map person = new HashMap(); person.put("name", "RiccioZhang"); person.put("age", "22"); person.put("addr", "GZ"); pageContext.setAttribute("person", person); %> 不使用标签迭代map:
<% for(Map.Entry entry : person.entrySet()){ pageContext.setAttribute("entry", entry); %> ${entry.key } = ${entry.value }
<% } %> 标签迭代--> Map:
<%-- varStatus保存着当前的迭代信息, 他有如下几个属性: * index 现在迭代成员的索引 * count 成员的总数 * first 当前是否为第一个成员 * last 当前是否为最后一个成员 可以用begin指定迭代从哪个索引开始, end指定从哪个索引处结束(包含), step指定迭代的步长。 --%> \${stat.index} = ${stat.index}
\${stat.count} = ${stat.count}
\${stat.first} = ${stat.first}
\${stat.last} = ${stat.last}
${en.key } = ${en.value }
-------------------------------------------------
标签迭代--> Collection:
<% Collection books = new HashSet(); 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); %> \${book} = ${book}
标签迭代--> array:
<% pageContext.setAttribute("colors", new String[]{"red", "blue", "green", "pink", "dark"}); %> \${color } = ${color }
<%-- 需求: 迭代Collection中的Map中的数组中的Collection. --%> <% Collection> coll = new ArrayList>(); Map m = new HashMap(); for(int i = 1; i <= 3; i++){ String key = "s" + i; Collection[] arrays = new Collection[i]; for(int j = 0; j < arrays.length; j++){ Collection c = new ArrayList(); c.add("a("+i+", "+j+")"); arrays[j] = c; } m.put(key, arrays); } coll.add(m); pageContext.setAttribute("coll", coll); %> ${entry.key } = [ ${s } ]
第${floor }楼

运行结果如下:

*******************************************************

--------------------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楼

*******************************************************

  • 标签

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

        items="stringOfTokens"
        delims="delimiters"
        [var="varName"]
        [varStatus="varStatusName"]
        [begin="begin"]
        [end="end"]
        [step="step"]>
      //body内容

         一步一步学JSP--JSTL(三)_第10张图片

	 --------------------c:forTokens-------------------------
\${d} = ${d }
<% pageContext.setAttribute("o", new Object()); %> \${d2} = ${d2 }

运行结果如下:

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

--------------------c:forTokens-------------------------
${d} = aa
${d} = bb
${d} = cc
${d2} = java.lang.Object
${d2} = 1eb01e5

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

  • 标签
在JSP页面进行URL的相关操作时,经常要在URL地址后面附加一些参数。标签可以嵌套在标签内,为这些标签所使用的URL地址附加参数。

标签在为一个URL地址附加参数时,将自动对参数值进行URL编码,例如,如果传递的参数值为“中国”,则将其转换为“%d6%d0%b9%fa”后再附加到URL地址后面,这也就是使用标签的最大好处。

示例:

  • 标签

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

一步一步学JSP--JSTL(三)_第11张图片

 --------------------c:import-------------------------
直接通过var获取:

通过varReader获取:
<% 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)); } %>
  • 标签
标签用于在JSP页面中构造一个URL地址,其主要目的是实现URL重写。URL重写就是将会话标识号以参数形式附加在URL地址后面

一步一步学JSP--JSTL(三)_第12张图片

	  --------------------c:url-------------------------
\${url } = ${url }

结果如下:

${url } = /day11/aaa/xxx.jsp?country=%e4%b8%ad%e5%9b%bd&city=%e6%ad%a6%e6%b1%89

  • 标签

标签用于实现请求重定向

一步一步学JSP--JSTL(三)_第13张图片

	 --------------------c:redirect-------------------------

3、自定义标签

3.1、什么是自定义标签

         自定义标签是指JSP自定义标签。自定义标签在功能上逻辑上与JavaBean类似,都封装Java代码。自定义标签是可重用的组件代码,并且允许开发人员为复杂的操作提供逻辑名称。自定义标签是JSP1.1规范里最早提出的。从标签的来源上看,JSP的标签库可以分为两种类型:一种是JSP标准标签库(JSTL,JSP Standard Tag Library),它是JSP开发环境供应商开发的。另一种是JSP开发环境的使用者(即用户)自己定义的标签。通过使用标签库,让JSP页面变得更加简洁,减少了JSP页面脚本代码量,大大降低JSP页面的复杂度,并且是代码最大程度是重用。

标签的几种形式:

  • 主体和内容都为空的:
  • 包含属性的标签:
  • 包含主体内容的标签:RiccioZhang
  • 包含主体内容和属性的标签:RiccioZhang
  • 嵌套的标签:

        
                     

标签库的接口和类的继承关系(标签库的api定义在javax.servlet.jsp.tagext下):

一步一步学JSP--JSTL(三)_第14张图片

开发自定义标签,其核心就是要编写处理器类,一个标签对应一个标签处理器类,而一个标签库则是很多标签处理器的集合。JSP所有的标签处理器类都实现javax.servlet.jsp.tagext.JspTag接口。这个接口是一个标记接口 。它有两个直接子接口:

  • 简单标签:标签处理类实现SimpleTag接口,它是JSP2.0新增加的接口,代表简单的标签。
  • 经典标签:JSP2.0以前标签处理类实现Tag接口,它是经典的必须实现的接口,它有一个直接子接口IterationTag

自定义标签执行流程:

一步一步学JSP--JSTL(三)_第15张图片

开发一个自定义的标签包含以下步骤:

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接口的标签处理器的生命周期:

一步一步学JSP--JSTL(三)_第16张图片

  • 自定义一个标签输出系统当前时间

(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




    my taglib
    my taglib
    1.0
    my
    http://www.ricciozhang.cn/jsp/jstl/my
    
    
    	date
    	cn.zq.tag.MyDateTag
    	
    	empty
    
(3) 配置tld文件(可选)

在web.xml文件中可以做如下的配置:


	
		
			http://www.ricciozhang.cn/jsp/jstl/my
			/WEB-INF/tld/my.tld
		
	

(4)使用标签

  引入标签库:<%@ taglib uri="http://www.ricciozhang.cn/jsp/jstl/my" prefix="my" %>

  使用:

说明:

  • 此标签类也必须要配置到tld文件中。
  • 对于SimpleTagSupport配置文件中的子元素,由于其主体不能包含Java程序片段,即它不处理body部分的内容,所以子元素的值不能为JSP.
  • 如果需要使用session等信息,可以先通过获取pageContext的方式获取:PageContext pc = (PageContext)getJspContext();

body-content元素说明:

Body-content元素可以包含的值为:

  • JSP:接收所有的JSP语法。
  • EMPTY:没有标签体
  • tagdependent:不解析body部分的标签。由自定义标签接收数据再行处理。若指定该值,标签体中的所有代码原封不动的交给标签处理器,而不是将执行结果传递给标签处理器
  • scriptless:接收EL,文本和JSP动作,但不接收<%= ..%>java脚本片段。

       如:如果使用scriptless则使用以下语法是错误的:行(2)
              1
              2    <%="Hello"%>
              3  

                

如果将第二行换成:就可以了

  • 定义一个标签输出最大值

        (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文件





    1.0
    my2
    http://www.ricciozhang.cn/jsp/jstl/my2
    
 	
 		max
 		cn.zq.tag.MyMaxValueTag
 		empty
 		
 			num1
 			true
 			
 			true
 			java.lang.Integer
 		
 		
 			num2
 			true
 			
 			true
 			java.lang.Integer
 		
 	

        (3)使用标签

  • 定义一个标签,带有三个属性,分别用于指定起始数、结束数和步长
        
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 +"
"); } } public JspTag getParent() { return null; } public void setJspBody(JspFragment jspBody) { } public void setParent(JspTag parent) { } }





    1.0
    my2
    http://www.ricciozhang.cn/jsp/jstl/my3
    
 	
 		circle
 		cn.zq.tag.CircleTag
 		empty
 		
 			step
 			true
 			
 			true
 			java.lang.Integer
 		
 		
 			end
 			true
 			
 			true
 			java.lang.Integer
 		
 		
 			begin
 			true
 			
 			true
 			java.lang.Integer
 		
 	

使用:

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


  •   定义一个标签,将标签体的内容全部转换成大写并输出
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;
	}

}





    1.0
    my4
    http://www.ricciozhang.cn/jsp/jstl/my4
    
 	
 		toUpperCase
 		cn.zq.tag.MyToUpperCaseTag
 		scriptless
 	

	
		asdfGDDDeyehehe
	

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

 	
 		forEach
 		cn.zq.tag.ForEachTag
 		scriptless
 		
 		
 			items
 			true
 			true
 			java.lang.Object
 		
 		
 			var
 			true
 			false
 			java.lang.String
 		
 	

<%
		pageContext.setAttribute("arrs", new int[]{1,2,3,4,5,6,7});
	%>
	
		${arr}
<% pageContext.setAttribute("list", Arrays.asList(new String[]{"zq", "zy", "lzh"})); %> ${item}
<% 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); %> ${entry.key } = ${entry.value }

关于简单标签的几点说明:  如果想要不执行标签下面的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文件然后加以修改。

	
 		curDate
 		cn.zq.tag.CurrentDateTag
 		empty
 	

  • 第三步:在web.xml中配置tld文件。(可选)在jsp-config中配置
  • 第四步:在jsp页面上引用自定义标签。使用<%@ taglib …%>引用自定义标签。
    <%@ taglib uri="http://www.ricciozhang.cn/jsp/jstl/zq" prefix="zq" %>
    


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

}

 	
 		BodyDemo
 		cn.zq.tag.BodyDemoTag
 		JSP
 	

	<%
		pageContext.setAttribute("name", "RiccoZhang");
	%>
	
		name = ${name }

创建和使用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....");
	}
}

	
 		simpleIterator
 		cn.zq.tag.SimpleIteratorTag
 		scriptless
 	

用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;
	}
}
 	
 		iteratorCollection
 		cn.zq.tag.IteratorCollectionTag
 		JSP
 		
 			items
 			true
 			true
 			java.util.Collection
 		
 		
 			var
 			true
 			false
 			java.lang.String
 		
 	

遍历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 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;
	}

}
 	 	
 		iteratorMap
 		cn.zq.tag.IteratorMapTag
 		JSP
 		
 			items
 			true
 			true
 			java.util.Map
 		
 		
 			var
 			true
 			false
 			java.lang.String
 		
 	

迭代所有类型的集合标签:

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

       
 		iterator
 		cn.zq.tag.IteratorTag
 		JSP
 		
 			items
 			true
 			true
 			java.lang.Object
 		
 		
 			var
 			true
 			false
 			java.lang.String
 		
 	

	----------iterator---------
${arr }
${l }
${entry.key } = ${entry.value }

  • 处理对象数组和基本类型数组

          对象数组:String[] ss = {…};

           保存基本数据的数组:int[] age={…}.
  •  对于对象数据:可以使用:

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

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

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

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

int len = Array.getLength(age);
List list = new  ArrayList();
for(int i=0;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文件中添加以下配置。


 
    1.0
 	fn
 	http://www.zq.cn/jsp/jstl/fn
 	
 	
 		add
 		cn.zq.fun.AddFun
 		int add(java.lang.String, java.lang.String)
 	


第四步:在页面引用

<%@ 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;
	}
}

 	
 		sayHi
 		cn.zq.fun.SayHiFuc
 		java.lang.String sayHi(java.lang.String)
 	
${fn:sayHi("RiccioZhang") }


关于标准函数库的相关内容,请查看fn.tld的相关内容



你可能感兴趣的:(java,java,web,java,web之旅,java,JSP,jstl,自定义标签)