最近朋友在自己做一个小型的ORM框架,想让我帮忙为他的数据结构定制一套专属的标记库,方便输出,看了下需求,和JSTL的forEach和out比较像。觉得比较简单,认为很快能搞定。但由于本人大三,只有一年左右的开发经验,经验不足,对定制标记库也只了解过,因此,在开发过程中遇到了很多问题。先贴上一段源码吧:
/**
*
* @author 张浩春 @time 2012-6-24 14:03:59
*/
public class ForEachTaglib extends BodyTagSupport {
private List<EasyData> items;//数据来源
private static String var;//对象名称
private String index;//下标名称
private static Integer i = 0;//下标
public static Integer getI() {
return i;
}
public static String getVar() {
return var;
}
public void setIndex(String index) {
this.index = index;
}
public void setItems(List<EasyData> items) {
this.items = items;
}
public void setVar(String var) {
ForEachTaglib.var = var;
}
@Override
public int doEndTag() throws JspException {
BodyContent body = getBodyContent();
JspWriter out = body.getEnclosingWriter();
String content = body.getString();
try {
out.println(content);//在循环结束后一次性输出内容
} catch (IOException ex) {
Logger.getLogger(ForEachTaglib.class.getName()).log(Level.SEVERE, null, ex);
}
var = null;//将变量置为空,释放空间
return EVAL_PAGE;
}
@Override
public int doAfterBody() throws JspException {
if (items != null) {
if (i >= items.size()) {//如果循环结束,那么必须返回SKIP_BODY常量结束循环,否则将死循环
i = 0;
return SKIP_BODY;
}
if (i == 0) {//第0将循环为空循环,需要将BODY清空
BodyContent body = getBodyContent();
body.clearBody();
}
super.pageContext.setAttribute(var, items.get(i));//将第i个对象存储到pageContext作用域下
if (index != null && !index.equals("")) {//如果用户指定了index的变量名称,则将下标存储到pageContext用域下
super.pageContext.setAttribute(index, i);
}
i++;
return EVAL_BODY_AGAIN;
} else {//如果数据来源为空,则跳出循环,否则会死循环导致内存溢出
BodyContent body = getBodyContent();
body.clearBody();
return SKIP_BODY;
}
}
}
再附上<h:out>的代码吧
@Override
public int doStartTag() throws JspException {
if (data != null) {//如果用户指定了数据来源,那么将优先输出该数据,输出完成直接返回
Object columnData;
if (column.matches("^[0-9]\\d*$")) {//如果用户填写的是数字,那么将按下标输出
try {
columnData = data.get(Integer.parseInt(column));
pageContext.getOut().println(columnData);//输出数据
} catch (Exception e) {
System.out.println("Column " + column + " in the data does not exist.");
}
} else {
try {
columnData = data.get(column);
pageContext.getOut().println(columnData);//按列名输出数据
} catch (Exception e) {
System.out.println("Column " + column + " in the data does not exist.");
}
}
return EVAL_PAGE;
}
if (ForEachTaglib.getVar() == null) {
return EVAL_PAGE;
}
if (super.pageContext.getAttribute(ForEachTaglib.getVar()) == null) {//此次数据为空,则直接返回
return EVAL_PAGE;
}
EasyData data = (EasyData) super.pageContext.getAttribute(ForEachTaglib.getVar());
if (column != null && !column.equals("")) {
Object columnData;
if (column.matches("^[0-9]\\d*$")) {//如果用户填写的是数字,那么将按下标输出
try {
columnData = data.get(Integer.parseInt(column));
pageContext.getOut().println(columnData);//输出数据
} catch (Exception e) {
System.out.println("Column " + column + " in the data does not exist.");
}
} else {
try {
columnData = data.get(column);
pageContext.getOut().println(columnData);//按列名输出数据
} catch (Exception e) {
System.out.println("Column " + column + " in the data does not exist.");
}
}
}
return EVAL_PAGE;
}
由于OUT标签是配合forEach一起使用的,为了方便起见,最开始out继承自forEach,正常情况下,其执行顺序应该为:
forEach:startTag
forEach:initBody
forEach:after
out:startTag
..
forEach:endTag
但实际上我的forEach:endTag却和after一起循环了,找了好久不知道为什么,改为不继承后就好了,哪位大神知道原因呢?求解释!!!
需要说明的是,在forEach中如果想循环,一定要在afterTag中返回EVAL_BODY_AGAIN.