自定义JSP标签02
12.4开发迭代标签
支持标签的类:
package org.lxh.tagdemo ;
import java.util.* ;
import javax.servlet.jsp.* ;
import javax.servlet.jsp.tagext.* ;
public class IterateTag extends TagSupport {
private String name ;
private String scope ;
private String id ; // 这个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<?>){
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 ;
}
}
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 void setName(String name){
this.name = name ;
}
public void setScope(String scope){
this.scope = scope ;
}
public void setId(String id){
this.id = id ;
}
public String getName(){
return this.name ;
}
public String getScope(){
return this.scope ;
}
public String getId(){
return this.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>mldntag</short-name>
<tag>
<name>present</name>
<tag-class>org.lxh.tagdemo.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>org.lxh.tagdemo.IterateTag</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>
<attribute>
<name>id</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>
使用XML映射tld文件的绝对路径:
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<display-name>Welcome to Tomcat</display-name>
<description>
Welcome to Tomcat
</description>
<jsp-config>
<taglib>
<taglib-uri>mldn_date</taglib-uri>
<taglib-location>/WEB-INF/datetag.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>mldn</taglib-uri>
<taglib-location>/WEB-INF/mldntag.tld</taglib-location>
</taglib>
</jsp-config>
<resource-ref>
<res-ref-name>jdbc/mldn</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
</web-app>
开发JSP文件
<%@ page contentType="text/html" pageEncoding="GBK"%>
<%@ page import="java.util.*"%>
<%@ taglib prefix="mytag" uri="mldn"%>
<html>
<head><title>www.mldnjava.cn,MLDN高端Java培训</title></head>
<body>
<% // 此代码只是为了测试,实际中此部分应该由servlet传递
List<String> all = new ArrayList<String>() ;
all.add("www.MLDN.cn") ;
all.add("www.MLDNJAVA.cn") ;
all.add("www.JIANGKER.com") ;
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代码。
12.5BodyTagSupport类
再本程序中,使用<jsp:useBean>标签定义了一个simple得属性名称,但是这个simple却可以想对象一样,可以直接在Scriptlet之间访问,而用户自己定义标签也想实现同样的效果,那么就需要通过TagExtraInfo类和VariableInfo类来完成。
为了更好说明此类,故完成下面定义一个变量的java类
package org.lxh.tagdemo ;
import java.util.* ;
import javax.servlet.jsp.* ;
import javax.servlet.jsp.tagext.* ;
public class BodyIterateTag extends BodyTagSupport {
private String name ;
private String scope ;
private String id ; // 这个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<?>){
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 ;
}
}
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 ;
}
}
public int doEndTag()
throws JspException{ // 如果此方法没有编写,则没有输出
if(super.bodyContent != null){
try{
super.bodyContent.writeOut(super.getPreviousOut()) ;
}catch(Exception e){
}
}
return BodyTagSupport.EVAL_PAGE ; // 正常执行完毕
}
public void setName(String name){
this.name = name ;
}
public void setScope(String scope){
this.scope = scope ;
}
public void setId(String id){
this.id = id ;
}
public String getName(){
return this.name ;
}
public String getScope(){
return this.scope ;
}
public String getId(){
return this.id ;
}
}
package org.lxh.tagdemo ;
import javax.servlet.jsp.tagext.* ;
public class BodyIterateTagExtraInfo extends TagExtraInfo {
public VariableInfo[] getVariableInfo(TagData data){
return new VariableInfo[] {new VariableInfo(data.getId(),"java.lang.String",true,VariableInfo.NESTED)} ;
}
}
该ltd文件如下:
<?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>mldntag</short-name>
<tag>
<name>present</name>
<tag-class>org.lxh.tagdemo.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>org.lxh.tagdemo.IterateTag</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>
<attribute>
<name>id</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<tag>
<name>bodyiterate</name>
<tag-class>org.lxh.tagdemo.BodyIterateTag</tag-class>
<tei-class>
org.lxh.tagdemo.BodyIterateTagExtraInfo
</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>
</taglib>
实际的工作中,对于标签的开发没有必要做深入的研究,因为有很多第三方开发的标签可以使用。
12.5简单标签的开发
为了简化标签开发的复杂度,专门增加了一个制作简单标签库的SimpleTagSupport类,直接覆写里面的doTag()方法即可完成。
下面开发一个之前开发的时间规格功能:
package org.lxh.tagdemo ;
import java.io.* ;
import java.util.* ;
import java.text.* ;
import javax.servlet.jsp.* ;
import javax.servlet.jsp.tagext.* ;
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){}
}
public void setFormat(String format){
this.format = format ;
}
public String getFormat(){
return this.format ;
}
}
标签操作:
<tag>
<name>simpledate</name>
<tag-class>org.lxh.tagdemo.SimpleDateTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>format</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>=
</tag>
XML文件的操作:
<taglib>
<taglib-uri>mldn_date</taglib-uri>
<taglib-location>/WEB-INF/datetag.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>mldn</taglib-uri>
<taglib-location>/WEB-INF/mldntag.tld</taglib-location>
</taglib>
直接编写JSP文件:
<%@ page contentType="text/html" pageEncoding="GBK"%>
<%@ taglib prefix="mytag" uri="mldn"%>
<html>
<head><title>www.mldnjava.cn,MLDN高端Java培训</title></head>
<body>
<h1>
<mytag:simpledate format="yyyy-MM-dd HH:mm:ss.SSS"/>
</h1>
</body>
</html>
此时标签的功能和之前完全一样,但是实现起来不用再处理各种复杂的返回值数据,直接编写即可。
但是也存在一个问题,返回值没有了,该怎么进行循环呢?
再进行迭代输出的时候,之前是通过doStartTag()和doAfterBody()操作的page属性范围,保存一个个的对象属性,但现在的操作中,可以直接通过getJspBody()调用标签体的内容。
package org.lxh.tagdemo ;
import java.io.* ;
import java.util.* ;
import java.text.* ;
import javax.servlet.jsp.* ;
import javax.servlet.jsp.tagext.* ;
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 void setId(String id){
this.id = id ;
}
public void setName(String name){
this.name = name ;
}
public void setScope(String scope){
this.scope = scope ;
}
public String getId(){
return this.id ;
}
public String getName(){
return this.name ;
}
public String getScope(){
return this.scope ;
}
}
tld文件的设置一样的:
<tag>
<name>simpleiterate</name>
<tag-class>org.lxh.tagdemo.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>
xml也是,不做重复。
再JSP文件中:
<%@ page contentType="text/html" pageEncoding="GBK"%>
<%@ page import="java.util.*"%>
<%@ taglib prefix="mytag" uri="mldn"%>
<html>
<head><title>www.mldnjava.cn,MLDN高端Java培训</title></head>
<body>
<%
List<String> all = new ArrayList<String>() ;
all.add("www.MLDN.cn") ;
all.add("www.MLDNJAVA.cn") ;
all.add("www.JIANGKER.com") ;
request.setAttribute("all",all) ;
%>
<h1>
<mytag:simpleiterate id="url" name="all" scope="request">
<h2>网站:${url}</h2>
</mytag:simpleiterate>
</h1>
</body>
</html>
此时代码功能已经实现了,可以发现,通过简单标签的标签库的开发,再也不用像之前那样处理各个复炸的返回值操作了。
技术这样的简单,但是标签库本身也是一个不常用的内容,所以掌握标签的使用的操作原理即可,而开发的使用建议不要使用。
12.6DynamicAttributes接口
之前的所有属性如果需要,则必须在<tag>文件中使用<attribute>的节点进行定义,但是如果现在属性是不固定的,可以由用户自己任意设置的话,就可以使用DynamicAttributes接口实现。
下面通过观察一个简单程序的实现来观察结果:
package org.lxh.tagdemo ;
import java.io.* ;
import java.util.* ;
import javax.servlet.jsp.* ;
import javax.servlet.jsp.tagext.* ;
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())) ;
}
}
tld文件的设置:
<tag>
<name>add</name>
<tag-class>org.lxh.tagdemo.DynamicAddTag</tag-class>
<body-content>empty</body-content>
<dynamic-attributes>true</dynamic-attributes>
</tag>
xml文件设置:
<jsp-config>
<taglib>
<taglib-uri>mldn_date</taglib-uri>
<taglib-location>/WEB-INF/datetag.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>mldn</taglib-uri>
<taglib-location>/WEB-INF/mldntag.tld</taglib-location>
</taglib>
</jsp-config>
JSP文件:
<%@ page contentType="text/html" pageEncoding="GBK"%>
<%@ taglib prefix="mytag" uri="mldn"%>
<html>
<head><title>www.mldnjava.cn,MLDN高端Java培训</title></head>
<body>
<h2>计算结果:
<mytag:add num1="11.2" num2="12.3" num3="13.5"/>
</h2>
</body>
</html>
使用动态属性,在进行标签调用的时候非常方便,但是如果是动态实现,则实现标签的难度也很大。
标签掌握的原理即可,因为下一章将讲解一些免费的标签组件。
2018 07 18
推荐阅读