本章目标
JSP标签编程
定义有属性的标签
本章目标
理解标签中熟悉的设置方法
理解标签配置文件中的属性定义格式
上面已经清楚的知道了,对于一个标签的开发需要标签的支持了,标签库的描述文件、web.xml文件中进行配置、之后在JSP中进行使用
但是对于属性:<jsp:forward page=""/>, 现在自己的标签也可以定义属性操作
下面定义一个可以完成日期格式化显示的操作,希望用户可以输入自己的日期的格式化的模板,根据此模板最终完成标签
package com.demo;
import java.util.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
public class IteratTag extends TagSupport {
private String name;
private String scope; // 这个id用于保存集合中的每一个元素
private String id;
private Iterator<?> iter = null;
public int doStartTag() throws JspException {
Object value = null;
if("page".equals(this.scope)) {
value = super.pageContext.getAttribute(this.name,pageContext.PAGE_SCOPE);
}
if("request".equals(this.scope)) {
value = super.pageContext.getAttribute(this.name,pageContext.REQUEST_SCOPE);
}
if("session".equals(this.scope)) {
value = super.pageContext.getAttribute(this.name,pageContext.SESSION_SCOPE);
}
if("application".equals(this.scope)) {
value = super.pageContext.getAttribute(this.name,pageContext.APPLICATION_SCOPE);
}
if(value != null && value instanceof List<?>) { //instanceof测试它左边的对象是否是它右边的类的实例,返回boolean类型的数据
this.iter = ((List<?>)value).iterator();
if(iter.hasNext()) {
//将属性保存在page属性范围之中
super.pageContext.setAttribute(this.id, iter.next());
return TagSupport.EVAL_BODY_INCLUDE;
} else {
return TagSupport.SKIP_BODY;
}
}else {
return TagSupport.SKIP_BODY;
}
}
@Override
public int doAfterBody() throws JspException {
if(iter.hasNext()) {
//将属性保存在page属性范围之中
super.pageContext.setAttribute(this.id, iter.next());
return TagSupport.EVAL_BODY_AGAIN; //反复执行doAfterBody()反复
} else {
return TagSupport.SKIP_BODY;
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
<?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_1.xsd"
version="2.1">
<tlib-version>1.0</tlib-version>
<short-name>firsttag</short-name>
<tag>
<name>present</name>
<tag-class>com.demo.AttributeTag</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>name</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>scope</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<tag>
<name>iterate</name>
<tag-class>com.demo.IteratTag</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>name</name>
<required>true</required>
<rtexprvale>true</rtexprvale>
</attribute>
<attribute>
<name>scope</name>
<required>true</required>
<rtexprvale>true</rtexprvale>
</attribute>
<attribute>
<name>id</name>
<required>true</required>
<rtexprvale>true</rtexprvale>
</attribute>
</tag>
</taglib>
<taglib>
<taglib-uri>gz</taglib-uri>
<taglib-location>/WEB-INF/gztag.tld</taglib-location>
</taglib>
<taglib>
<%@ page contentType="text/html; charset=GBK" %>
<%@ page import="java.util.*"%>
<%@ taglib prefix="mytag" uri="gz"%>
<html>
<head>
<title>欢迎光临</title>
</head>
<body>
<%
List<String> all = new ArrayList<String>();
all.add("你好吗");
all.add("天天快乐");
all.add("神仙日子");
request.setAttribute("all",all); //将内存保存在标签执行
%>
<mytag:present name="all" scope="request">
<mytag:iterate id="url" name="all" scope="request">
<h3>生活:${url}</h3>
</mytag:iterate>
</mytag:present>
</body>
</html>
此处的操作比较简单,主要就是为了演示各个方法之间的操作,以及为了以后讲解Struts 提供更加方便的原理操作
本程序是直接采用了List 集合的方式完成的,而实际之中可能会传递Map 或者是对象数组等等,这些功能的实现过程基本上是一样的
而且此时一个JSP 页面也的确符合了MVC的设计准则,代码非常少,而且不包括Scriptlet代码
小结
迭代标签在实际的开发中使用较多,尤其是在Struts 框架中更被广泛使用
迭代标签需要用户处理标签体中的内容
body-content的值有下面4种:
<xsd:enumeration value="tagdependent"/>
<xsd:enumeration value="JSP"/>
<xsd:enumeration value="empty"/>
<xsd:enumeration value="scriptless"/>
tagdependent:标签体内容直接被写入BodyContent,由自定义标签类来进行处理,而不被JSP容器解释,
如下:
<test:myList>
select name,age from users
</test:myList>
JSP:接受所有JSP语法,如定制的或内部的tag、scripts、静态HTML、脚本元素、JSP指令和动作。如:
<my:test>
<%=request.getProtocol()%> // ②
</my:test>
具体可参考后面附源码。
empty:空标记,即起始标记和结束标记之间没有内容。
下面几种写法都是有效的,
<test:mytag />
<test:mytag uname="Tom" />
<test:mytag></test:mytag>
scriptless:接受文本、EL和JSP动作。如上述②使用<body-content> scriptless </body-content>则报错,具体可参考后面附源码。
第十二章 JSP标签编程 -------BodyTagSupport 类
掌握BodyTagSupport 类的基本操作流程
掌握TagExtraInfo 类和VariableInfo 类的作用
可以使用BodyTagSupport 类完成迭代标签的开发
BodyTagSupport
BodyTagSupport是TagSupport 类的子类,通过继承BodyTagSupport 类实现的标签可以直接处理标签体的数据,BodTagSupport 类的定义如下:
public class BodyTagSupport extends TagSupport implements BodyTag
TagExtraInfo l类和VariableInfo类
在本程序中,使用<jsp:useBean> 标签定义了一个simple 的属性名称,但是这个simple却看像对象一样,可以直接在Scriptlet 之中访问,而如果用户自己定义的标签也想实现同样的效果,那么就需要通过TagExtraInfo类和VariableInfo类完成了
为了更好的说明BodySupport类, 以及与便利相关的两个类的关系,那么下面使用此类修改之前的迭代标签
package com.demo;
import java.util.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
public class BodyIteratorTag extends BodyTagSupport {
private String name;
private String scope; // 这个id用于保存集合中的每一个元素
private String id;
private Iterator<?> iter = null;
public int doStartTag() throws JspException {
Object value = null;
if("page".equals(this.scope)) {
value = super.pageContext.getAttribute(this.name,pageContext.PAGE_SCOPE);
}
if("request".equals(this.scope)) {
value = super.pageContext.getAttribute(this.name,pageContext.REQUEST_SCOPE);
}
if("session".equals(this.scope)) {
value = super.pageContext.getAttribute(this.name,pageContext.SESSION_SCOPE);
}
if("application".equals(this.scope)) {
value = super.pageContext.getAttribute(this.name,pageContext.APPLICATION_SCOPE);
}
if(value != null && value instanceof List<?>) { //instanceof测试它左边的对象是否是它右边的类的实例,返回boolean类型的数据
this.iter = ((List<?>)value).iterator();
if(iter.hasNext()) {
//将属性保存在page属性范围之中
super.pageContext.setAttribute(this.id, iter.next());
return BodyTagSupport.EVAL_BODY_BUFFERED;
} else {
return BodyTagSupport.SKIP_BODY;
}
}else {
return BodyTagSupport.SKIP_BODY;
}
}
@Override
public int doAfterBody() throws JspException {
if(iter.hasNext()) {
//将属性保存在page属性范围之中
super.pageContext.setAttribute(this.id, iter.next());
return BodyTagSupport.EVAL_BODY_AGAIN; //反复执行doAfterBody()反复
} else {
return BodyTagSupport.SKIP_BODY;
}
}
@Override
public int doEndTag() throws JspException { //如果此方法没有编写则没有输出
if(super.bodyContent != null) {
try {
super.bodyContent.writeOut(super.getPreviousOut());
} catch (Exception e) {
}
}
return BodyTagSupport.EVAL_PAGE; //正常执行完毕
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
package com.demo;
import javax.servlet.jsp.tagext.TagData;
import javax.servlet.jsp.tagext.TagExtraInfo;
import javax.servlet.jsp.tagext.VariableInfo;
// 标签变量的处理类
public class BodyIteratorTagExtraInfo extends TagExtraInfo {
@Override
public VariableInfo[] getVariableInfo(TagData data) {
return new VariableInfo[] {new VariableInfo(data.getId(),
"java.lang.String",true,VariableInfo.NESTED)};
}
}
<tag>
<name>bodyiterate</name>
<tag-class>com.demo.BodyIteratorTag</tag-class>
<tei-class>
com.demo.BodyIteratorTagExtraInfo
</tei-class>
<body-content>JSP</body-content>
<attribute>
<name>name</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>scope</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>id</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<%@ page contentType="text/html; charset=GBK" %>
<%@ page import="java.util.*"%>
<%@ taglib prefix="mytag" uri="gz"%>
<html>
<head>
<title>欢迎光临</title>
</head>
<body>
<%
List<String> all = new ArrayList<String>();
all.add("你好吗");
all.add("天天快乐");
all.add("神仙日子");
request.setAttribute("all",all); //将内存保存在标签执行
%>
<mytag:present name="all" scope="request">
<mytag:bodyiterate id="url" name="all" scope="request">
<h3>生活:${url}、<%=url.length()%></h3>
</mytag:bodyiterate>
</mytag:present>
</body>
</html>
此时的url 已经成为一个变量存在,而这个便利的使用,必须依靠:BodyIteratorTagExtraInfo 支持才可以完成
此时的代码已经明显比以前更加复杂了,所以在世界中基本上使用TagSupport 类比较多的
实际开发之中,对于标签的开发没有必要做深入的研究,因为有很大第三方的标签可以使用
小结
使用BodyTagSupport 类可以完成标签的开发,但是开发的过程要比直接使用TagSupport 类麻烦许多
通过TagExtraInfo 类和VariableInfo 可以直接将标签中定义的属性变为一个变量操作
简单标签
在JSP1.2之前如果要想进行标签库的开发,要么西安站继承TagSupport 类要么就是继承BodyTagSupport 类,而且还要去覆写里面的doStartTag()、doAfterBody()、doEndTag(),而且还要非常清楚这些方法的返回值类型,例如: SKIP_BODY、EVAL_BODY_INCLUDE等,这对于用户的开发而言实战在太麻烦了,所以到了JSP2.0 之后,为了简化标签开发 的复杂度,专门增加了一个制作简单标签库的SimpleTagSupport类,直接覆写里面的doTag() 方法即可完成
SimpleTagSupport 类的定义如下:
public class SimpleTagSupport extends Object implements SimpleTag
SimpleTagSupport 类一网打尽之前的所有复杂的标签开发,再也不用去区分什么doxxTag()方法了,而直接就是doTag() 方法。
下面开发一个之前的格式化日期的程序
package com.demo;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class SimpleDateTag extends SimpleTagSupport {
private String format; //接收格式化
public void doTag() throws JspException, IOException {
SimpleDateFormat sdf = new SimpleDateFormat(this.format);
try {
super.getJspContext().getOut().write(sdf.format(new Date() ));
} catch (Exception e) {
e.printStackTrace();
}
super.doTag();
}
public String getFormat() {
return format;
}
public void setFormat(String format) {
this.format = format;
}
}
<tag>
<name>simple</name>
<tag-class>com.demo.SimpleDateTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>format</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<%@ page contentType="text/html; charset=GBK"%>
<%@ taglib prefix="mytag" uri="gz"%>
<html>
<head>
<title>欢迎光临</title>
</head>
<body>
<h1>
<mytag:simple format="yyyy-MM-dd HH:mm:ss.SSS"/>
</h1>
</body>
</html>
此时的标签的功能域之前完全一样,但是实现起来不用再处理复杂的返回值数据,直接缩写即可。
在进行迭代输出的时候,之前是通过doStartTag() 和 doAfterBody() 操作的page属性范围,保存的一个个的对象属性,但是现在的操作中,可以直接too你哥哥getJspBody(),调用标签中的内容
package com.demo;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class SimpleIterateTag extends SimpleTagSupport {
private String id;
private String name;
private String scope;
public void doTag() throws JspException, IOException {
Object value = null;
if("page".equals(this.scope)) {
value = super.getJspContext().getAttribute(this.name,PageContext.PAGE_SCOPE);
}
if("request".equals(this.scope)) {
value = super.getJspContext().getAttribute(this.name,PageContext.REQUEST_SCOPE);
}
if("session".equals(this.scope)) {
value = super.getJspContext().getAttribute(this.name,PageContext.SESSION_SCOPE);
}
if("application".equals(this.scope)) {
value = super.getJspContext().getAttribute(this.name,PageContext.APPLICATION_SCOPE);
}
if(value != null && value instanceof List<?>) {
Iterator<?> iter = ((List<?>) value).iterator();
while(iter.hasNext()) {
super.getJspContext().setAttribute(id, iter.next());
super.getJspBody().invoke(null);
}
}
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
}
<tag>
<name>simpleiterate</name>
<tag-class>com.demo.SimpleIterateTag</tag-class>
<body-content>scriptless</body-content>
<attribute>
<name>name</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>scope</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>id</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<%@ page contentType="text/html; charset=GBK" %>
<%@ page import="java.util.*"%>
<%@ taglib prefix="mytag" uri="gz"%>
<html>
<head>
<title>欢迎光临</title>
</head>
<body>
<%
List<String> all = new ArrayList<String>();
all.add("你好吗");
all.add("天天快乐");
all.add("神仙日子");
request.setAttribute("all",all); //将内存保存在标签执行
%>
<h1>
<mytag:simpleiterate id="url" name="all" scope="request">
<h2>${url}</h2>
</mytag:simpleiterate>
</h1>
</body>
</html>
此时的代码功能就已经实现了,可以发现,通过简单标签完成的标签库的开发,再也不用像之前的那样需要处理各个复杂的返回值的操作了
即便是这样的简单,但是标签库本身也是一个不常用的内容,所以,掌握标签的使用的操作原理即可,而开发的使用建议不用使用
小结
通过SimpleTagSupport 类实现的标签库操作要比使用TagSupport 类容易许多,而且也不用再娶处理各个复杂的返回值类型
DynamicAttributes 接口
之前所有的属性如果需要,则必须在<tag> 文件中使用<attribute> 的节点进行定义,但是如果现在属性不是固定的,可以由用户自己任意设置的话,就可以使用DynamicAttribute 接口实现
package com.demo;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.DynamicAttributes;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class DynamicAddTag extends SimpleTagSupport implements DynamicAttributes {
private Map<String, Float> num = new HashMap<String, Float>();
public void doTag() throws JspException, IOException {
Float sum = 0.0f;
Iterator<Map.Entry<String, Float>> iter = this.num.entrySet().iterator();
while(iter.hasNext()) {
Map.Entry<String, Float> value = iter.next();
sum += value.getValue(); // 取出每一个内容
}
super.getJspContext().getOut().write(sum + "");
}
public void setDynamicAttribute(String uri, String localName, Object value)
throws JspException {
// 取出设置的每一个动态属性都保存在Map集合里面
num.put(localName, Float.parseFloat(value.toString()));
}
}
<tag>
<name>add</name>
<tag-class>com.demo.DynamicAddTag</tag-class>
<body-content>empty</body-content>
<dynamic-attributes>true</dynamic-attributes>
</tag>
<%@ page contentType="text/html; charset=GBK"%>
<%@ taglib prefix="mytag" uri="gz"%>
<html>
<head>
<title>欢迎光临</title>
</head>
<body>
<h3>
<mytag:add num1="22.2" num2="11.1" num3="33.3"/>
</h3>
</body>
</html>
使用动态的属性,在进行标签调用的时候非常的方便,但是如果是动态实现,则实现标签的难度也很大