使用Java来开发JSP标记
最近复习JSP中。我们知道JSP是Java WEB的一个规范。在这个规范之内我们可以自己去做很多事情。JSP API提供了接口允许我们自己去自定义JSP标签和EL表达式。自定义JSP标签的大致思想是这样的,实现一个包含标签参数和行为逻辑的类,这个类我们称它为标记处理器类,它实质上是一个Bean,我们设置标签的属性实际上是调用它的setter方法。接下来通过使用标记库描述文件(TLD)来描述标签的相关信息,以便在JSP页面中使用taglib指令时能顺利的找到它。下面我们来看看详细应该怎么去做。
从JSP2.0开始引入了一个简单标记处理器接口——javax.servlet.jsp.tagext.SimpleTag接口,这个接口取代了原先JSP1.0提供的3个标记处理器接口,我们只需要使用SimpleTarget接口就可以实现所有类型的JSP标记。之所以称之为简单,是因为这样设计让开发者在编写程序上省了不少力气,但是它可以实现很复杂的功能,并不是说它的功能很简单。
该接口包含了如下5个方法:
void doTag(); //该方法包含了标签执行的业务逻辑
JspTag getParent(); //获取该标签的父标签
setJspBody(JspFragment body); //设置JSP标签体,关于JspFragment类我们稍后再讨论
setJspContext(JspContext ctx);//设置JSP上下文
setParent(JspTag parent); //设置父标签
但是多数情况下我们不需要去亲自实现这个接口,因为作为开发者,我们关心的是业务逻辑,也就是doTag()方法的内容,这种情况我们只需要继承javax.servlet.jsp.tagext.SimpleTagSupport类即可,这个类为我们实现了SimpleTag接口,我们只需要去重写它的doTag()方法。下面我们来实现一个最简单的标签:给标签传递一个参数,让它在页面上显示出来.
首先我们要做的是写一个标记处理器类:
package test.jsp.tag.simple;
import java.io.IOException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class HelloTag extends SimpleTagSupport{
private String name;
public void setName(String name){
this.name = name;
}
@Override
public void doTag()throws IOException{
this.getJspContext().getOut().print("Hello! "+name);
}
}
嗯,这是一个继承了SimpleTagSupport类的Bean,一样拥有属性、setter方法。唯一特别的是重写了父类的doTag()方法。在这个doTag()中,我们通过获得一个JSP上下文对象来向页面输出一句话:Hello!XXX。
下面是该介绍JspContext对象的时候了,这是一个抽象类,它唯一的子类是PageContext。这么说刚才所返回的实际对象是PageContext类型的。我们可以对其进行强制转换:PageContext pageCtx = (PageContext)this.getJspContext();
通过PageContext对象我们就可以访问到Request和Response对象了,如:
HttpServletRequest request = (HttpServletRequest)pageCtx.getRequest();
HttpServletResponse response = (HttpServletResponse)pageCtx.getResponse();
通过这种方式我们就可以在标签处理器中操作WEB应用各个作用域的数据了。
我们都知道在使用JSP标签时要在JSP页面开头使用taglib指令进行声明,如:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
taglib指令通过URL来对标签进行定位。但是它是如何做到的呢?这就要靠TLD了,TLD就是标记库描述文件(Tag Library Descriptor,TLD),我们看JSTL的JAR包中,META-INF文件夹下每个库都有一个对应的部署描述文件。TLD实际上是一个XML文件,里面描述了标记库和其中标记的信息。一个完整的TLD文件结构如下所示:
<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">
<tlib-version>1.1</tlib-version>
<short-name>test</short-name>
<uri>http://test</uri>
<tag>
<name>hello</name>
<tag-class>test.jsp.tag.simple.HelloTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>name</name>
<required>true</required>
</attribute>
</tag>
<tag>
<name>if</name>
<tag-class>test.jsp.tag.simple.IfTag</tag-class>
<body-content>scriptless</body-content>
<attribute>
<name>test</name>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<tag>
<name>foreach</name>
<tag-class>test.jsp.tag.simple.ForeachTag</tag-class>
<body-content>scriptless</body-content>
<attribute>
<name>items</name>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>var</name>
</attribute>
</tag>
<function>
<description>
Just for test
</description>
<name>add</name>
<function-class>test.jsp.el.AddEl</function-class>
<function-signature>int add(int,int)</function-signature>
</function>
</taglib>
起始标签是taglib,<tlib-version>定义了taglib的版本号,<short-name>定义了标签库的简称,<uri>定义了URI标识,taglib就是通过此URI来找到相关的标记库的。
接下来的<tag>标签便是定义JSP标签的属性了:
以下是必须的属性
<name>标签名称
<tag-class>标记器类
<body-content>动作体类型,JSP2.0支持如下动作体:
empty 空标记
jsp:可以包含标签、EL和Scriptlet
scriptless:不允许Java脚本内容
tagdependent:对体不进行处理
<attribute>标签定义了标签参数。
其中:
<name>标签指定了参数的名称。
<required>指定了参数是否是必要的。true是必要,false是不必要
<rtexprvalue>指定了是否允许使用表达式(包括EL表达式和Java表达式),true为允许,false为不允许
<funtion>标签定义了EL表达式,我们稍后再做介绍。
如何执行动作体?
我们在编写JSP页面时经常会用到<c:if>和<c:forEach>之类的标签
<c:if test="true">
<p>条件是真的,执行!</p>
</c:if>
只要满足条件,就会去执行动作体,那么我们怎么在自定义JSP标记中去执行动作体呢?
首先我们先要来研究一个叫做JspFragment的类。JspFragment代表了JSP标签的动作体,我们通过它的invoke方法就可以执行动作体,下面我们来实现一个类似于<c:if>的标签:
package test.jsp.tag.simple;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class IfTag extends SimpleTagSupport{
private boolean test;
public void setTest(boolean test){
this.test = test;
}
@Override
public void doTag()throws IOException,JspException{
JspFragment body = getJspBody();
//如果成立则执行相应逻辑
if(test){
body.invoke(null);
}
}
}
只要满足了条件,标签动作体就会执行。
invoke方法接受一个java.io.Writer对象,使用这个对象将内容输出到页面,如果不输出则可以给它传递一个null参数。
JSP中引入了EL表达式给我们的开发带来了很大的方便,同自定义标签一样,允许开发者去自定义EL表达式函数,定义EL表达式函数要比定义标签简单多了,我们甚至不需要去实现任何接口,假设我们要实现一个简单的EL表达式函数来计算两个数的和,就可以这样:
package test.jsp.tag.simple;
public class Calculator {
public static int add(int a,int b){
return a+b;
}
public static int minus(int a,int b){
return a-b;
}
public static int multiply(int a,int b){
return a*b;
}
public static int divide(int a,int b){
return a/b;
}
}
这个类中全部是static方法,这是一个工具类。
然后我们只需要在TLD声明即可:
<function>
<description>
Just for test
</description>
<name>add</name>
<function-class>test.jsp.el.AddEl</function-class>
<function-signature>int add(int,int)</function-signature>
</function>
function-signature类似于C语言中的函数声明,通过这个找到到底调用类中哪个工具方法。
使用的时候只需要这样就可以了:${test:add(1,2)}运行时输出结果为3