第六章_编写定制标签

6.1、定制标签概述

利用jsp标准动作指令访问和操作javaBeans,是首次尝试将表现代码和业务逻辑实现分离。但是,标准动作指令的功能不够强大,单独使用时,开发者经常要使用jsp页面中的java代码。例如,标准动作指令无法像jstlforEach标签那样迭代集合。

认识到了用javabean分离表现逻辑和业务逻辑的不足之处之后,jsp1.1就定义了定制标签。定制标签具有javaBeans所没有的优势。例如,定制标签可以访问jsp隐式对象,可以带有属性等。

 

6.2、简单的标签处理器

jsp2.0中,他们在javax.servlet.jsp.tagext包中添加了新的接口:simpleTag。实现SimpleTag接口的标签处理器称作简单的标签处理器,实现TagIterationBodyTag接口的标签处理器称作典型的标签处理器。

简单的标签处理器的声明周期更加简单,并且更容易。SimpleTag接口中只有一个方法:doTag,并且在标签调用时只执行一次。业务逻辑、迭代及主体操作代码都要在这里编写。简单的标签处理器中的主体是用一个JspFragment类实例表示的。


一个简单的标签处理器的生命周期如下:

1、jsp容器通过调用其无参构造器,创建一个简单标签处理器实例。因此,简单的标签处理器必须有一个无参构造器。

2、JSP容器调用setJspContext方法,同时传递一个JspContext对象。JspContext最重要的方法是getOut,它返回一个JspWriter,用于将响应发送到客户端。setJspContext方法的签名如下:

Public void setJspContext(JspContext jspContext)

大多数时候,会需要将传进的JspContext赋给一个类变量,以便供后续使用。

3、如果表示标签处理器的定制标签是嵌套在另一个标签中的,jsp容器就会调用setParent方法。该方法具有以下签名:

Public void setParent(JspTag parent)

4、JSP容器为给该标签定义的每个属性都调用设置方法。(setter

5、如果标签中有主体内容,JSP将调用SimpleTag接口的setJspBody方法,将主体内容作为JspFragment传递。如果没有主体内容,JSP容器则不会调用这个方法。

6、JSP容器调用doTag方法。所有变量在doTag方法返回时进行同步。

 

6.3SimpleTag实例

下面是我的测试代码的项目结构。


下面是我的代码:

package customtag;

import java.io.IOException;

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 MyFirstTag implements SimpleTag{
	JspContext jspContext ;
	public void doTag() throws JspException, IOException {
		// TODO Auto-generated method stub
		System.out.println("doTag");
		jspContext.getOut().print("This is my first tag.") ;
	}

	public JspTag getParent() {
		// TODO Auto-generated method stub
		System.out.println("getParent");
		return null;
	}

	public void setJspBody(JspFragment arg0) {
		// TODO Auto-generated method stub
		System.out.println("set JspBody");
	}

	public void setJspContext(JspContext arg0) {
		// TODO Auto-generated method stub
		System.out.println("setJspContext");
		this.jspContext = arg0 ;
	}

	public void setParent(JspTag arg0) {
		// TODO Auto-generated method stub
		System.out.println("set parent");
	}
	
}

<?xml version="1.0" encoding="utf-8"?>
<taglib xmlnx="http://java.sun.com/xml/ns/j2ee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2eeweb-jsptaglibrary_2_1.xsd"
	version="2.1">
	<description>
		Simple tag examples
	</description>
	<tlib-version>1.0</tlib-version>
	<short-name>My First Taglib Example</short-name>
	<tag>
		<name>firstTag</name>
		<tag-class>customtag.MyFirstTag</tag-class>
		<body-content>empty</body-content>
	</tag>
</taglib>

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="easy" uri="/WEB-INF/mytags.tld" %>
<html>
  <head>
    <title>Testing my first tag</title>
  </head>
  
  <body>
    Hello!!!
    <br />
    <easy:firstTag/>
  </body>
</html>

6.4、处理属性

实现SimpleTag接口或者继承SImpleTagSupport的标签处理器可以带有属性。

下面是项目结构图:


下面是程序代码:

package customtag;

import java.io.IOException;
import java.util.StringTokenizer;

import javax.servlet.jsp.JspContext;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.SimpleTagSupport;

public class DataFormatterTag extends SimpleTagSupport{
	private String header ;
	private String items ;
	
	public void setHeader(String header) {
		this.header = header;
	}

	public void setItems(String items) {
		this.items = items;
	}

	@Override
	public void doTag() throws JspException, IOException {
		JspContext jspContext = getJspContext() ;
		JspWriter out = jspContext.getOut() ;
		out.print("<table style='border:1px solid green'>\n"
				+"<tr><td><span style='font-wright:bold'>"
				+ header + "</span></td></tr>\n") ;
		StringTokenizer tokenizer = new StringTokenizer(items, ",") ;
		while(tokenizer.hasMoreTokens()){
			String token = tokenizer.nextToken() ;
			out.print("<tr><td>" + token + "</td></tr>\n") ;
		}
		out.print("</table>") ;
	}
	
}

<?xml version="1.0" encoding="utf-8"?>  
<taglib xmlnx="http://java.sun.com/xml/ns/j2ee"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2eeweb-jsptaglibrary_2_1.xsd"  
    version="2.1"> 
    <tlibversion>2.1</tlibversion>
	<jspversion>2.1</jspversion>
    <tag>
    	<name>dataFormatter</name>
    	<tag-class>customtag.DataFormatterTag</tag-class>
    	<body-content>empty</body-content>
    	<attribute>
    		<name>header</name>
    		<required>true</required>
    	</attribute>
    	<attribute>
    		<name>items</name>
    		<required>true</required>
    	</attribute>
    </tag>
</taglib> 

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="/WEB-INF/mytags.tld" prefix="easy"%>
<html>
  <head>
    <title>Testing DataFormatterTag</title>
  </head>
  
  <body>
    <easy:dataFormatter items="Alabama,Alaska,Georgia,Florida" header="States"/>
    <br/>
    <easy:dataFormatter header="Countries">
    	<jsp:attribute name="items">
    		US,UK,Canada,Korea
    	</jsp:attribute>
    </easy:dataFormatter>
  </body>
</html>

运行结果图:



6.5、管理标签主体

有了SImpleTag,就可以通过JSP闯过来的JspFragment管理标签主体了。JspFragment类表示一段JSP代码,可以不调用,也可以调用多次。JSP片段的定义中不能包含Scriplet或者Scriptlet表达式,它只能包含模版文本和JSP动作指令元素。

JspFragment类有两个方法:getJspContextinvoke,其方法签名如下:

Public abstract JspContext getJspContext()

Public abstract void invoke(java.io.Writer writer)throws JspException, java.io.IOException

getJspContext方法返回与这个JspFragment相关的JspContext。我们可以调用invoke方法来执行片段(标签主体),并将所有输出内容导到指定的Writer。如果传给invoke方法的值为null,那么输出的结果将会被导到与该片段相关的JspContextgetOut方法所返回的JspWriter

下面是代码段:

package customtag;

import java.io.IOException;

import javax.servlet.jsp.JspContext;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.SimpleTagSupport;

public class SelectElementTag extends SimpleTagSupport{
	private String[] countries = {"Australia", "Brazil", "China"} ;
	@Override
	public void doTag() throws JspException, IOException {
		// TODO Auto-generated method stub
		JspContext jspContext = getJspContext() ;
		JspWriter out = jspContext.getOut() ;
		out.print("<select>\n") ;
		for(int i=0;i<3; i++){
			getJspContext().setAttribute("value", countries[i]) ;
			getJspContext().setAttribute("text", countries[i]) ;
			getJspBody().invoke(null) ;
		}
		out.print("</select>\n") ;
	}
}

<?xml version="1.0" encoding="utf-8"?>  
<taglib xmlnx="http://java.sun.com/xml/ns/j2ee"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2eeweb-jsptaglibrary_2_1.xsd"  
    version="2.1"> 
    <tlibversion>2.1</tlibversion>
	<jspversion>2.1</jspversion>
    <tag>
    	<name>dataFormatter</name>
    	<tag-class>customtag.DataFormatterTag</tag-class>
    	<body-content>empty</body-content>
    	<attribute>
    		<name>header</name>
    		<required>true</required>
    	</attribute>
    	<attribute>
    		<name>items</name>
    		<required>true</required>
    	</attribute>
    </tag>
    <tag>
    	<name>select</name>
    	<tag-class>customtag.SelectElementTag</tag-class>
    	<body-content>scriptless</body-content>
    </tag>
</taglib> 

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="/WEB-INF/mytags.tld" prefix="easy" %>
<html>
  <head>
    <title>My JSP 'selectElementTagTest.jsp' starting page</title>
  </head>
  
  <body>
    <easy:select>
    	<option value="${value }">${text }</option>
    </easy:select>
  </body>
</html>

输出结果显示:



6.6、编写EL函数

一般来说,编写一个EL函数要遵循以下两个步骤:

1、创建一个包含讲台方法的public类。每个静态方法表示一个函数。这个类不需要实现接口或者继承类。你可以根据需要,像对待其他任何类一样部署这个类。这个类必须保存到WEB-INF/classes目录或其下面的某个目录中。

2、利用function元素在标签类库描述符中注册函数。

Function元素必须直接放在taglib元素下,并且可以带有以下子元素:

description。这是一条特定于标签的可选信息。

display-nameXML工具显示的简称。

iconXML工具可以使用的可选图标元素。

name。该函数独特的名称。

function-class。实现该函数的java类的全类名。

function-signature。表示该函数的静态java方法签名。

example。使用该函数的一个example的可选信息描述。

function-extension。通过XML工具使用,没有扩展名,或者有多个扩展名,提供关于该函数的其他信息。

使用函数时,需利用taglib指令及其uri属性,它指向标签类库描述符,以及要使用的前缀。然后在jsp页面中利用以下语法调用函数:

${prefix:functionName(parameterList)}

下面是关于EL函数的例子:

package function;

public class StringFunctions {
	public static String reverseString(String s){
		return new StringBuffer(s).reverse().toString() ;
	}
}

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="/WEB-INF/functions.tld" prefix="f" %>
<html>
  <head>
    <title>My JSP 'reverseStringFunctionTest.jsp' starting page</title>
  </head>
  
  <body>
  	${f:reverseString("Hello World")}
  </body>
</html>

<?xml version="1.0" encoding="utf-8"?>  
<taglib xmlnx="http://java.sun.com/xml/ns/j2ee"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2eeweb-jsptaglibrary_2_1.xsd"  
    version="2.1"> 
    <tlibversion>2.1</tlibversion>
	<jspversion>2.1</jspversion>
	 <function>
    	<description>
    		Reverse a String
    	</description>
    	<name>
    		reverseString
    	</name>
    	<function-class>
    		function.StringFunctions
    	</function-class>
    	<function-signature>
    		java.lang.String reverseString(java.lang.String)
    	</function-signature>
    </function>
</taglib>

在浏览器输入:http://localhost:8089/servlet/reverseStringFunctionTest.jsp



6.7、发布定制标签

我们可以将定制标签处理器和标签类库描述符打包成一个jar文件,以便发给其他人使用,像jstl一样。在这种情况下,就需要包含所有的标签处理器,以及描述它们的tld文件。此外,还需要在描述符的uri元素中指定一个绝对的URL

例如,我就打包成一个cc.jar文件。Jar文件内容如图所示。


可以直接利用cmd的jar命令打包,如图:




这样我们就可以把jar包复制到项目的lib目录下,并且jsp中引用刚才的uri地址。




项目运行截图:



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值