在Struts中我们经常这样循环的打印Message
<logic:messagesPresent message="true">
<html:messages id="msg" message="true">
<div class="success">
<bean:write name="msg"/>
</div><br/>
</html:messages>
</logic:messagesPresent>
查阅struts tag的文档我们看到了关于messagesPresent的message属性注释如下
message | By default the tag will retrieve the request scope bean it will iterate over from the |
也就是说在将message设置为true时,会去request中寻找Globals.MESSAGE_KEY所代表的bean,然而我们在具体的Action使用ActionMessages类的时候往往是这样的
ActionMessages messages = getErrors(request);
messages.add(ActionMessages.GLOBAL_MESSAGE , new ActionMessage(key , value0));
saveMessages(request,messages);
而往往困扰人的就在于为什么要给messages中放入名称为"ActionMessages.GLOBAL_MESSAGE"的ActionMessage对象,而且还需要再次的调用saveErrors(request,messages)方法?
首先要说明的是你为ActionMessage起任何的名称都没有关系,因为ActionMessages本身维持着一个HashMap,而参数property就是这个HashMap中的key值,如果不存在则会建立相应的key,并将需要保存的ActionMessage对象存入到这个key所对应的List中。
public void add(String property, ActionMessage message) {
ActionMessageItem item = (ActionMessageItem) messages.get(property);
List list = null;
if (item == null) {
list = new ArrayList();
item = new ActionMessageItem(list, iCount++, property);
messages.put(property, item);
} else {
list = item.getList();
}
list.add(message);
}
至于为什么一定要调用saveMessages(request,messages)?看看它具体的实现逻辑就清楚了
protected void saveMessages(
HttpServletRequest request,
ActionMessages messages) {
// Remove any messages attribute if none are required
if ((messages == null) || messages.isEmpty()) {
request.removeAttribute(Globals.MESSAGE_KEY);
return;
}
// Save the messages we need
request.setAttribute(Globals.MESSAGE_KEY, messages);
}
再对比前面介绍的messagesPresent标签的使用,是不是就清楚了呢?原来它是将ActionMessages对象保存在request中,并且名称是Globals.ERROR_KEY!从而为tag的顺利解析铺平了道路。当然按理你可以选择将这样的对象放置在任何的scope中,但Action只是提供了request , session两种Scope(不过page , application不经常使用,可以理解,但不提供相应的结构就不太好了)
至于messagesPresent标签是如何在scope中寻找ActionMessages对象
org.apache.struts.taglib.logic.MessagesPresentTag
protected boolean condition(boolean desired) throws JspException {
ActionMessages am = null;
String key = name;
if (message != null && "true".equalsIgnoreCase(message)){
key = Globals.MESSAGE_KEY;
}
try {
am = TagUtils.getInstance().getActionMessages(pageContext, key);
} catch (JspException e) {
TagUtils.getInstance().saveException(pageContext, e);
throw e;
}
Iterator iterator = (property == null) ? am.get() : am.get(property);
return (iterator.hasNext() == desired);
}
org.apache.struts.taglib.TagUtils
public ActionErrors getActionErrors(PageContext pageContext, String paramName)
throws JspException {
ActionErrors errors = new ActionErrors();
Object value = pageContext.findAttribute(paramName);
if (value != null) {
try {
if (value instanceof String) {
errors.add(
ActionMessages.GLOBAL_MESSAGE,
new ActionMessage((String) value));
} else if (value instanceof String[]) {
String keys[] = (String[]) value;
for (int i = 0; i < keys.length; i++) {
errors.add(
ActionMessages.GLOBAL_MESSAGE,
new ActionMessage(keys[i]));
}
} else if (value instanceof ActionErrors) {
errors = (ActionErrors) value;
} else {
throw new JspException(
messages.getMessage(
"actionErrors.errors",
value.getClass().getName()));
}
} catch (JspException e) {
throw e;
} catch (Exception e) {
log.debug(e, e);
}
}
return errors;
}
PageContext中的findAttribute会帮你在scope中寻找名称为Globals.MESSAGE_KEY的ActionMessage对象。
注意
虽然Struts已经声明:不推荐使用ActionErrors & ActionError对象,但在一些遗留的系统中,依然还是可以看到其影子,所以如果你的系统不幸属于这样的两种混合系统,有以下的几种方法可以参考
1。两次调用messagesPresent,如下
<!-- Print ActionErrors Object -->
<logic:messagesPresent>
<html:messages id="msg" message="true">
<div class="success">
<bean:write name="msg"/>
</div><br/>
</html:messages>
</logic:messagesPresent>
<!-- Print ActionMessages Object -->
<logic:messagesPresent message="true">
<html:messages id="msg" message="true">
<div class="success">
<bean:write name="msg"/>
</div><br/>
</html:messages>
</logic:messagesPresent>
2.分别使用<html:messages> <html:errors>标签,当然在老系统中需要调用Action的saveErrors方法,而在新的应用中要调用saveMessages方法。
3.更换所有的ActionErrors为ActionMessages,并将所有调用saveErrors的地方更换成saveMessages,并将<html:errors>标签相应的更换成<html:messages message="true"> - 推荐!