从ActionErrors过渡到ActionMessages

Posted on 2006-06-27 23:02 killvin 阅读(1544) 评论(0)  编辑  收藏 所属分类: java

在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 Globals.ERROR_KEY constant string, but if this attribute is set to 'true' the request scope bean will be retrieved from the Globals.MESSAGE_KEY constant string. Also if this is set to 'true', any value assigned to the name attribute will be ignored


也就是说在将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"> - 推荐!