6.1、定制标签概述
利用jsp标准动作指令访问和操作javaBeans,是首次尝试将表现代码和业务逻辑实现分离。但是,标准动作指令的功能不够强大,单独使用时,开发者经常要使用jsp页面中的Java代码。例如,标准动作指令无法像jstl的forEach标签那样迭代集合。
认识到了用javabean分离表现逻辑和业务逻辑的不足之处之后,jsp1.1就定义了定制标签。定制标签具有javaBeans所没有的优势。例如,定制标签可以访问jsp隐式对象,可以带有属性等。
6.2、简单的标签处理器
在jsp2.0中,他们在javax.servlet.jsp.tagext包中添加了新的接口:simpleTag。实现SimpleTag接口的标签处理器称作简单的标签处理器,实现Tag、Iteration或BodyTag接口的标签处理器称作典型的标签处理器。
简单的标签处理器的声明周期更加简单,并且更容易。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.3、SimpleTag实例
下面是我的代码:
- 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类有两个方法:getJspContext和invoke,其方法签名如下:
Public abstract JspContext getJspContext()
Public abstract void invoke(java.io.Writer writer)throws JspException, java.io.IOException
getJspContext方法返回与这个JspFragment相关的JspContext。我们可以调用invoke方法来执行片段(标签主体),并将所有输出内容导到指定的Writer。如果传给invoke方法的值为null,那么输出的结果将会被导到与该片段相关的JspContext的getOut方法所返回的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-name。XML工具显示的简称。
icon。XML工具可以使用的可选图标元素。
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地址。
项目运行截图: