`

JavaWeb之自定义标签

 
阅读更多

 

内容提纲

 

自定义标签的 HelloWorld

带属性的自定义标签

带标签体的自定义标签

带父标签的自定义标签

EL自定义函数

 

自定义标签的 HelloWorld

 

开始之前先看一个例子

 

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

	<% 
		List<Customer> customers = new ArrayList<Customer>();
		customers.add(new Customer(1, "AAA"));
		customers.add(new Customer(2, "BBB"));
		customers.add(new Customer(3, "CCC"));
		customers.add(new Customer(4, "DDD"));
		customers.add(new Customer(5, "EEE"));
		
		request.setAttribute("customers", customers);
	%>
	
	<%
	    List<Customer> cs = (List<Customer>)request.getAttribute("customers");
	    for(Customer c : cs){
	%>
	    <b>--<%=c.getId() %> -- <%=c.getName() %></b> <br>
	<%}%>
	
	<hr/>
	
	<c:forEach items="${requestScope.customers }" var="customer">
		<b>--${pageScope.customer.id } -- ${customer.name }</b> <br>
	</c:forEach>	
	<br><br>
	
</body>
</html>

  

 以上代码中,在<hr/>上下用两种方式遍历list,可以看到采用自定义标签更加简洁易读。

 

提出问题

 

自定义标签可以降低 jsp 开发的复杂度和维护量,从 html 角度来说,可以使 html 不用去过多的关注那些比较复杂的商业逻辑(业务逻辑)。

利用自定义标签,可以软件开发人员和页面设计人员合理分工:页面设计人员可以把精力集中在使用标签(HTML,XML或者JSP)创建网站上,而软件开发人员则可以将精力集中在实现底层功能上面,如国际化等,从而提高了工程生产力

将具有共用特性的tag库应用于不同的项目中,体现了软件复用的思想。 

 

什么是自定义标签 

 

用户定义的一种自定义的jsp标记 。当一个含有自定义标签的jsp页面被jsp引擎编译成servlet时,tag标签被转化成了对一个称为 标签处理类 的对象的操作。于是,当jsp页面被jsp引擎转化为servlet后,实际上tag标签被转化为了对tag处理类的操作。 

 

标签库 API

 

标签库 API 定义在 javax.servlet.jsp.tagext 包中

 



 

 

传统标签和简单标签

 

开发自定义标签,其核心就是要编写处理器类,一个标签对应一个标签处理器类,而一个标签库则是很多标签处理器的集合。所有的标签处理器类都要实现 JspTag 接口,该接口中没有定义任何方法,主要作为 Tag 和 SimpleTag 接口的父接口。

在 JSP 2.0 以前,所有标签处理器类都必须实现 Tag 接口,这样的标签称为传统标签。

JSP 2.0 规范又定义了一种新的类型的标签,称为简单标签,其对应的处理器类要实现 SimpleTag 接口

 

 

标签的形式

 

空标签:<hello/>

带有属性的空标签:

   <max num1=“3” num2=“5”/>

带有内容的标签:

  <greeting>

       hello

  </greeting>

带有内容和属性的标签:

 <greeting name=“Tom”>

       hello

  </greeting>

 

  

自定义标签的开发与应用步骤

  

编写完成标签功能的 Java 类(标签处理器)

编写标签库描述(tld)文件,在tld文件中对自定义中进行描述

在 JSP 页面中导入和使用自定义标签

 

示例

 

	<!-- 把value的值打印count次 -->
	<rabbitx_helloworld:hello value="helloworld" count="5"/>

 

1.  添加标签处理类

 

package org.rabbitx.web.javaweb.tag;

import java.io.IOException;

import javax.servlet.jsp.JspContext;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.JspTag;
import javax.servlet.jsp.tagext.SimpleTag;

public class HelloSimpleTag implements SimpleTag {

	private String value;
	private String count;
	
	public void setValue(String value) {
		this.value = value;
	}
	
	public void setCount(String count) {
		this.count = count;
	}
	
	@Override
	public void doTag() throws JspException, IOException {
		
		JspWriter out = pageContext.getOut();
		int c = 0;
		
		c = Integer.parseInt(count);
		for(int i = 0; i < c; i++){
			out.print((i + 1) + ": " + value);
			out.print("<br>");
		}
	}

	@Override
	public JspTag getParent() {
		return null;
	}

	@Override
	public void setJspBody(JspFragment arg0) {
	}

	private PageContext pageContext;
	
	@Override
	public void setJspContext(JspContext arg0) {
		this.pageContext = (PageContext) arg0;
	}

	@Override
	public void setParent(JspTag arg0) {
	}

}

 

 2. 编写标签库描述(tld)文件

 

<?xml version="1.0" encoding="UTF-8" ?>

<taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
	version="2.0">

	<!-- 描述 TLD 文件 -->
	<description>Rabbitx helloworld Tag 1.0 core library</description>
	<display-name>RabbitxTag helloworld</display-name>
	<tlib-version>1.0</tlib-version>

	<!-- 建议在 JSP 页面上使用的标签的前缀 -->
	<short-name>rabbitx_helloworld</short-name>
	<!-- 作为 tld 文件的 id, 用来唯一标识当前的 TLD 文件, 多个 tld 文件的 URI 不能重复. 通过 JSP 页面的 taglib 
		标签的 uri 属性来引用. -->
	<uri>http://www.rabbitx.org/rabbitx/helloworld</uri>
	
	<!-- 描述自定义的 HelloSimpleTag 标签 -->
	<tag>
		<!-- 标签的名字: 在 JSP 页面上使用标签时的名字 -->
		<name>hello</name>

		<!-- 标签所在的全类名 -->
		<tag-class>org.rabbitx.web.javaweb.tag.HelloSimpleTag</tag-class>
		<!-- 标签体的类型 -->
		<body-content>empty</body-content>

		<!-- 描述当前标签的属性 -->
		<attribute>
			<!-- 属性名 -->
			<name>value</name>
			<!-- 该属性是否被必须 -->
			<required>true</required>
			<!-- rtexprvalue: runtime expression value 当前属性是否可以接受运行时表达式的动态值 -->
			<rtexprvalue>true</rtexprvalue>
		</attribute>

		<attribute>
			<name>count</name>
			<required>false</required>
			<rtexprvalue>false</rtexprvalue>
		</attribute>
	</tag>
	
</taglib>  

 

 3. 在 JSP 页面中导入和使用自定义标签

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="rabbitx_helloworld" uri="http://www.rabbitx.org/rabbitx/helloworld" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

	<!-- 把value的值打印count次 -->
	<rabbitx_helloworld:hello value="helloworld" count="5"/>
	
</body>
</html>

 

 

小结

 

2. 自定义标签

 

1). HelloWorld

 

①. 创建一个标签处理器类: 实现 SimpleTag 接口. 

②. 在 WEB-INF 文件夹下新建一个 .tld(标签库描述文件) 为扩展名的 xml 文件. 并拷入固定的部分: 并对 

description, display-name, tlib-version, short-name, uri 做出修改

 

<taglib xmlns="http://java.sun.com/xml/ns/j2ee"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"

    version="2.0">

    

  <description>JSTL 1.1 core library</description>

  <display-name>JSTL core</display-name>

  <tlib-version>1.1</tlib-version>

  <short-name>c</short-name>

  <uri>http://java.sun.com/jsp/jstl/core</uri>

  

</taglib>

 

③. 在 tld 文件中描述自定义的标签:

 

<!-- 描述自定义的 HelloSimpleTag 标签 -->

  <tag>

  <!-- 标签的名字: 在 JSP 页面上使用标签时的名字 -->

  <name>hello</name>

 

  <!-- 标签所在的全类名 -->

  <tag-class>com.atguigu.javaweb.tag.HelloSimpleTag</tag-class>

  <!-- 标签体的类型 -->

  <body-content>empty</body-content>

  </tag>

  

④. 在 JSP 页面上使用自定义标签: 

 

> 使用 taglib 指令导入标签库描述文件: <%@taglib uri="http://www.atguigu.com/mytag/core" prefix="atguigu" %>

 

> 使用自定义的标签: <atguigu:hello/> 

 

2). setJspContext: 一定会被 JSP 引擎所调用, 先于 doTag, 把代表 JSP 引擎的 pageContext 传给标签处理器类. 

 

private PageContext pageContext;

 

@Override

public void setJspContext(JspContext arg0) {

System.out.println(arg0 instanceof PageContext);  

this.pageContext = (PageContext) arg0;

}

 

3). 带属性的自定义标签:

 

①. 先在标签处理器类中定义 setter 方法. 建议把所有的属性类型都设置为 String 类型. 

 

private String value;

private String count;

 

public void setValue(String value) {

this.value = value;

}

 

public void setCount(String count) {

this.count = count;

}

 

②. 在 tld 描述文件中来描述属性:

 

<!-- 描述当前标签的属性 -->

<attribute>

<!-- 属性名, 需和标签处理器类的 setter 方法定义的属性相同 -->

<name>value</name>

<!-- 该属性是否被必须 -->

<required>true</required>

<!-- rtexprvalue: runtime expression value 

当前属性是否可以接受运行时表达式的动态值 -->

<rtexprvalue>true</rtexprvalue>

</attribute>

 

③. 在页面中使用属性, 属性名同 tld 文件中定义的名字. 

 

<atguigu:hello value="${param.name }" count="10"/>

 

4). 通常情况下开发简单标签直接继承 SimpleTagSupport 就可以了. 可以直接调用其对应的 getter 方法得到对应的 API 

 

public class SimpleTagSupport implements SimpleTag{

    

    public void doTag() 

        throws JspException, IOException{}

    

    private JspTag parentTag;

    

    public void setParent( JspTag parent ) {

        this.parentTag = parent;

    }

    

    public JspTag getParent() {

        return this.parentTag;

    }

    

    private JspContext jspContext;

    

    public void setJspContext( JspContext pc ) {

        this.jspContext = pc;

    }

    

    protected JspContext getJspContext() {

        return this.jspContext;

    }

    

    private JspFragment jspBody;

                

    public void setJspBody( JspFragment jspBody ) {

        this.jspBody = jspBody;

    }

    

    protected JspFragment getJspBody() {

        return this.jspBody;

    }   

}

 

 

 SimpleTag 接口

 

setJspContext 方法:该方法把代表 JSP 页面的 pageContext 对象传递给标签处理器对象。

setParent 方法:该方法把父标签处理器对象传递给当前标签处理器对象

getParent 方法:该方法用于获得标签的父标签处理器对象

setJspBody 方法:该方法用于把代表标签体的 JspFragment 对象传递给标签处理器对象

doTag 方法:该方法用于完成所有的标签逻辑。该方法可以抛出 javax.servlet.jsp.SkipPageException 异常,用于通知 web 容器不再执行 JSP 页面中位于结束标记后面的内容。

 

 

实现 SimpleTag 接口的标签处理器类的生命周期

 



  

 

 

 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

 

为简化简单标签处理器的编写工作,JSP API 中提供了

      SimpleTag接口的一个实现类SimpleTagSupport。

      SimpleTagSupport实现了SimpleTag接口中的方法,它

      内部以成员变量的形式保存了setJspContext方法和setJspBody方法传递进来的参数。此外,它还定义了如

      下两个方法、来返回这两个参数:

getJspContext方法:该方法用于返回代表调用页面的JspContext对象

getJspBody方法:该方法用于得到代表标签体的JspFragment对象,

 

 

标签库描述文件

 

标签库描述(Tag Library Description)文件简称为 tld 文件,其扩展名为 .tld

多个标签的集合就形成了一个标签库,标签库中的所有标签都必须在标签文件中进行描述

Tld  文件可以放置在 web 应用程序的 WEB-INF 目录及其子目录中,但不能放置在 WEB-INF 目录下的 classes 和 lib 子目录中 。tld 文件也可以放置在 WEB-INF\lib 目录下的 jar 包的 META-INF 目录及其子目录中

 



  

 

<body-content>:指定标签体的类型。可能取值有 3 种:

empty:没有标签体

scriptless:标签体可以包含 el 表达式和 JSP 动作元素,但不能包含 JSP 的脚本元素

tagdependent:表示标签体交由标签本身去解析处理。若指定 tagdependent,在标签体中的所有代码都会原封不动的交给标签处理器,而不是将执行结果传递给标签处理器

 

 

在 JSP 页面引用自定义标签

 

在 JSP 页面使用 taglib 指令引入标签库描述文件:

   <%@ taglib prefix=“” uri=“” %>

uri:属性用于指定所引入的标签库描述(tld)文件中所定义的 <uri> 元素的内容;prefix 属性用于为引入的 tld 文件指定一个”引用代号”。Prefix 属性可以由 jsp 文件的作者任意指定,只要与其他 taglib 指令的 prefix 属性值不同就可以。

 

案例

 

定制一个带有两个属性的标签<max>, 用于计算并输出两个数的最大值

    <rabbitx:max para2="35" para1="53"/>

    

定制一个带有一个属性的标签<file>, 用于输出指定文件的内容

<rabbitx:file src="/WEB-INF/classes/data.txt"/>

 

定制一个用于迭代输出集合(List, Set)中所有元素的标签:

<rabbitx_fragment:foreach items="${requestScope.users}" var="user">

        ${pageScope.user.id} -- ${user.name} -- ${user.password} <br/>

</rabbitx_fragment:foreach> 

 

定制一个没有属性的标签< rabbitx: htmlFilter filter=“”></rabbitx : htmlFilter>, 用于过滤标签体中的 html 特殊字符

 



  

 

EL 自定义函数

 

EL 自定义函数:在 EL 表达式中调用的某个 Java 类的静态方法,这个静态方法需在 web 应用程序中进行配置才可以被 EL 表达式调用。

EL 自定义函数可以扩展 EL 表达式的功能,让 EL 表达式完成普通 Java 程序代码所能完成的功能。

 

EL 自定义函数开发步骤

 

编写 EL 自定义函数映射的Java 类中的静态方法:这个 Java 类必须带有 public 修饰符,方法必须是这个类的带有 public 修饰符的静态方法

编写标签库描述文件(tld 文件), 在 tld 文件中描述自定义函数

在 JSP 页面中导入和使用自定义函数

 

在 tld 文件中描述 EL 自定义函数

 



  

 

为了能够让一个 Java 类的静态方法可以被 EL 表达式调用,需要在一个标签库描述文件(tld 文件)中对 EL 自定义函数进行描述,以将 Java 类中的静态方法映射成一个 EL 自定义函数

在 JSP 页面中导入和使用 EL 自定义函数

在标准 JSP 页面中使用 taglib 指令来引入 tld 文件:

<%@ taglib uri="/petrelskyTag" prefix="petrelsky"%>

uri:属性用于指定所引入的标签库描述(tld)文件中所定义的 <uri> 元素的内容;

prefix 属性用于为引入的 tld 文件指定一个”引用代号”。Prefix 属性可以由 jsp 文件的作者任意指定,只要与其他 taglib 指令的 prefix 属性值不同就可以。

调用 EL 自定义函数: ${petrelsky :toGBK (param.username) } 

 

 

编写 2 个 EL 自定义函数

计算输入字符串的长度

完成对字符串的截取:substr(str, fromIndex, length) abcdefghijk, 2,2  cd

 

jstl fn 函数 

 

为了简化在 JSP 页面操作字符串,JSTL 中提供了一套 EL 自定义函数,这些自定义函数包含了 JSP 页面制经常要用到的字符串操作

在JSTL的表达是中要使用一个函数,其格式如下

   ${ns:methodName(args....)}

在使用这些函数之前必须在JSP中引入标准函数的声明

  <%@ taglib prefix="fn"   uri="http://java.sun.com/jsp/jstl/functions" %> 

 



 



  

 

 小结

 

3. JSTL:

 

1)*. c:out 主要用于对特殊字符进行转换. 真正进行输出时, 建议使用 c:out, 而不是使用 EL

2)*. c:set: 可以为域赋属性值。 而对域对象中的 JavaBean 的属性赋值用的并不多. 

3). c:remove: 移除指定域对象的指定属性值(较少使用, 即便移除也是在 Servlet 中完成)

 

4)*. c:if: 在页面上对现实的内容进行过滤, 把结果存储到域对象的属性中. 但不灵活, 会被其他的自定义标签所取代. 

5). c:choose, c:when, c:otherwise: 作用同上, 但麻烦, 不灵活.

 

6)*. c:forEach: 对集合进行遍历的. 常用!

7). c:forTokens: 处理字符串, 类似于 String 累的 split() 方法(知道即可)

 

8). c:import: 导入页面到当前页面的. (了解)

9). c:redirect: 当前页面进行重定向的. (使用较少)

10)*. c:url: 产生一个 URL 的, 可以进行 URL 重写, 变量值编码, 较为常用. 

 

2. 开发有父标签的标签:

 

1). 父标签无法获取子标签的引用, 父标签仅把子标签作为标签体来使用. 

 

2). 子标签可以通过 getParent() 方法来获取父标签的引用(需继承 SimpleTagSupport 或自实现 SimpleTag 接口的该方法):

若子标签的确有父标签, JSP 引擎会把代表父标签的引用通过  setParent(JspTag parent)  赋给标签处理器

 

3). 注意: 父标签的类型是 JspTag 类型. 该接口是一个空接口, 但是来统一 SimpleTag 和 Tag 的. 实际使用需要进行类型的强制转换.

 

4). 在 tld 配置文件中, 无需为父标签有额外的配置. 但, 子标签是是以标签体的形式存在的, 所以父标签的 <body-content></body-content>

需设置为 scriptless

 

5). 实现 

 

<c:choose>

<c:when test="${param.age > 24}">大学毕业</c:when>

<c:when test="${param.age > 20}">高中毕业</c:when>

<c:otherwise>高中以下...</c:otherwise>

</c:choose>

 

> 开发 3 个标签: choose, when, otherwise

> 其中 when 标签有一个 boolean 类型的属性: test

> choose 是 when 和 otherwise 的父标签

> when 在 otherwise 之前使用

 

> 在父标签 choose 中定义一个 "全局" 的 boolean 类型的 flag: 用于判断子标签在满足条件的情况下是否执行. 

 

* 若 when 的 test 为 true, 且 when 的父标签的 flag 也为 true, 则执行 when 的标签体(正常输出标签体的内容), 

    同时把 flag 设置为 false

* 若 when 的 test 为 true, 且 when 的父标签的 flag 为 false, 则不执行标签体. 

* 若 flag 为 true, otherwise 执行标签体. 

 

1. 带标签体的自定义标签: 

 

1). 若一个标签有标签体: 

 

<atguigu:testJspFragment>abcdefg</atguigu:testJspFragment>

 

在自定义标签的标签处理器中使用 JspFragment 对象封装标签体信息. 

 

2). 若配置了标签含有标签体, 则 JSP 引擎会调用 setJspBody() 方法把 JspFragment 传递给标签处理器类

在 SimpleTagSupport 中还定义了一个 getJspBody() 方法, 用于返回 JspFragment 对象. 

 

3). JspFragment 的 invoke(Writer) 方法: 把标签体内容从 Writer 中输出, 若为 null, 

则等同于 invoke(getJspContext().getOut()), 即直接把标签体内容输出到页面上

 

有时, 可以 借助于 StringWriter, 可以在标签处理器类中先得到标签体的内容: 

 

//1. 利用 StringWriter 得到标签体的内容.

StringWriter sw = new StringWriter();

bodyContent.invoke(sw);

 

//2. 把标签体的内容都变为大写

String content = sw.toString().toUpperCase();

 

4). 在 tld 文件中, 使用 body-content 节点来描述标签体的类型: 

 

<body-content>: 指定标签体的类型, 大部分情况下, 取值为 scriptless。可能取值有 3 种:

empty: 没有标签体

scriptless: 标签体可以包含 el 表达式和 JSP 动作元素,但不能包含 JSP 的脚本元素

tagdependent: 表示标签体交由标签本身去解析处理。

若指定 tagdependent,在标签体中的所有代码都会原封不动的交给标签处理器,而不是将执行结果传递给标签处理器

 

<body-content>tagdependent</body-content>

 

5). 定义一个自定义标签: <atguigu:printUpper time="10">abcdefg</atguigu> 把标签体内容转换为大写, 并输出 time 次到

浏览器上. 

 

6). 实现 forEach 标签: 

 

> 两个属性: items(集合类型, Collection), var(String 类型)

 

> doTag: 

 

* 遍历 items 对应的集合

* 把正在遍历的对象放入到 pageContext 中, 键: var, 值: 正在遍历的对象. 

* 把标签体的内容直接输出到页面上. 

 

    <c:forEach items="${requestScope.customers }" var="cust2">

${pageScope.cust2.id } -- ${cust2.name } <br>

</c:forEach>

 

<atguigu:saveAsFile src="d:\\haha.txt">

abcde

</atguigu>

 

  • 大小: 27.3 KB
  • 大小: 49.4 KB
  • 大小: 159 KB
  • 大小: 57.2 KB
  • 大小: 99.4 KB
  • 大小: 110.7 KB
  • 大小: 118 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics