logic:Iterator標簽(以下簡稱“該標簽”)是Struts裡非常常用的一個標簽,其作用在於循環顯示給定容器對象中的值
如此常用的標簽,其源代碼當然需要拿出來研究一下,以下列舉幾條研究成果:
1、該標簽內部使用Collection來表示給定的容器,所有的給定容器對象(如ArrayList,Map等)都會被其轉化成為Collection
2、該標簽自己維護循環索引
3、該標簽常見的幾個屬性如下:
name、property、scope、id
4、結合以上標簽,給出一段源代碼來解釋其工作的機理
這段源代碼中,一開始就可以看到這樣一句:
collection = TagUtils.getInstance().lookup(pageContext, name, property, scope);
這局代碼在之前的幾次Struts源碼分析中已經分析到了,作用如下:
1、如果property的值為null,那麼在scope定義的范圍內(即request、session、application、page)查找以name變量值命名的對象(返回值是一個Object,然後轉化Collection)
2、如果property的值不為null,那麼做完1步驟的事情後,她將調用org.apache.commons.beanutils.PropertyUtils類中的getProperty方法,得到目標對象,轉化成Collection類型所以,我們在編碼時,可以自己構建一個ArrayList,然後放到session或request范圍內,然後在logic:Iterator標簽中可以這樣定義:
name=對象在session或request中綁定的key值property可以不寫(因為沒有再將這個ArrayList包裝進一個對象)
scope也可以不寫(不寫將發生pageContext.findAttribute方法調用,在四種scope中依次尋找),或寫session或request之後的代碼也很好理解,Struts得到Collection之後,動態判斷其進一步的類型,然後調用相關方法獲得Iterator最後,Struts使用得到的Iterator對象,開始對Collection進行循環,將Collection中每個元素對象取出,以id變量值綁定到pageContext上。看到這裡,心急的人可能會問,怎麼就這麼結束了麼?她不將元素對象取出,然後顯示麼?
public int doStartTag() throws JspException {
// Acquire the collection we are going to iterate over
Object collection = this.collection;
if (collection == null) {
collection = TagUtils.getInstance().lookup(pageContext, name, property, scope);
}
if (collection == null) {
JspException e = new JspException(messages.getMessage("iterate.collection"));
TagUtils.getInstance().saveException(pageContext, e);
throw e;
}
// Construct an iterator for this collection
if (collection.getClass().isArray()) {
try {
// If we're lucky, it is an array of objects
// that we can iterate over with no copying
iterator = Arrays.asList((Object[]) collection).iterator();
} catch (ClassCastException e) {
// Rats -- it is an array of primitives
int length = Array.getLength(collection);
ArrayList c = new ArrayList(length);
for (int i = 0; i < length; i++) {
c.add(Array.get(collection, i));
}
iterator = c.iterator();
}
} else if (collection instanceof Collection) {
iterator = ((Collection) collection).iterator();
} else if (collection instanceof Iterator) {
iterator = (Iterator) collection;
} else if (collection instanceof Map) {
iterator = ((Map) collection).entrySet().iterator();
} else if (collection instanceof Enumeration) {
iterator = IteratorUtils.asIterator((Enumeration) collection);
} else {
JspException e = new JspException(messages.getMessage("iterate.iterator"));
TagUtils.getInstance().saveException(pageContext, e);
throw e;
}
// Calculate the starting offset
if (offset == null) {
offsetValue = 0;
} else {
try {
offsetValue = Integer.parseInt(offset);
} catch (NumberFormatException e) {
Integer offsetObject = (Integer) TagUtils.getInstance().lookup(pageContext, offset, null);
if (offsetObject == null) {
offsetValue = 0;
} else {
offsetValue = offsetObject.intValue();
}
}
}
if (offsetValue < 0) {
offsetValue = 0;
}
// Calculate the rendering length
if (length == null) {
lengthValue = 0;
} else {
try {
lengthValue = Integer.parseInt(length);
} catch (NumberFormatException e) {
Integer lengthObject = (Integer) TagUtils.getInstance().lookup(pageContext, length, null);
if (lengthObject == null) {
lengthValue = 0;
} else {
lengthValue = lengthObject.intValue();
}
}
}
if (lengthValue < 0) {
lengthValue = 0;
}
lengthCount = 0;
// Skip the leading elements up to the starting offset
for (int i = 0; i < offsetValue; i++) {
if (iterator.hasNext()) {
iterator.next();
}
}
// Store the first value and evaluate, or skip the body if none
if (iterator.hasNext()) {
Object element = iterator.next();
if (element == null) {
pageContext.removeAttribute(id);
} else {
pageContext.setAttribute(id, element);
}
lengthCount++;
started = true;
if (indexId != null) {
pageContext.setAttribute(indexId, new Integer(getIndex()));
}
return (EVAL_BODY_TAG);
} else {
return (SKIP_BODY);
}
}
不急,其實該標簽做到這一步已經可以了,因為在我們使用logic:Iterator標簽的同時,一般還會使用bean:write標簽,如下一段:
<logic:iterate id="myuserinfo" name="browseresult" scope="request">
<tr>
<td align="center">
<bean:write name="myuserinfo" property="username" filter="true"/>
</td>
<td align="center">
<bean:write name="myuserinfo" property="userdesc" filter="true"/>
</td>
</tr>
</logic:iterate>
所以,bean:write這個標簽將會到pageContext裡面,以id變量值為key值,查找這個元素對象,然後將其屬性(property屬性定義)取出、顯示。
OK,至此,應該已經很清楚了。最後還要一提id這個屬性,很多example中可能會對我們有誤解,認為id屬性的定義也對應一個實體類(實體bean),其實不然,通過以上的源代碼,可以看到,id這個屬性只是一個key而已,Struts用這個值來將Collection中的每個元素對象綁定到pageContext裡面去,所以,對於id屬性的值,完全可以自定義,只要遵守一條規則:
在logic:Iterator標簽中定義的id屬性值必須和下面bean:write標簽中的name屬性的值一致!