SpringMVC生成VM或FreeMarker时处理Model属性报错分析

出错现象

近期在维护一个使用vm作为页面视图的项目时,遇到过这样一个错误。

org.apache.catalina.core.StandardWrapperValve.invoke Servlet.service() for servlet [springServlet] in context with path [/portal-web] threw exception
[Cannot expose request attribute ‘attr’ because of an existing model object of the same name] with root cause javax.servlet.ServletException: Cannot expose request attribute ‘processInst’ because of an existing model object of the same name

根据字面意思,Spring试图往实现了Model接口的对象(例如BinddingAwareModelMap,继承关系:–> ExtendedModelMap –> ModelMap ,实现Model接口)里放入request对象的一个attribute时,遇到了已经重名的attribute,因此报该错误。

但是根据我我们以往编码往Model里面放装入属性的经验来看,是能够自动覆盖同名属性的。

原因初步分析

根据SpringMVC的文档,如果要把request和session中的attribute放到model里面让前端使用,可以使用在VelocityLayoutViewResolver中配置下面的属性来实现。

<property name="exposeRequestAttributes" value="true" />
<property name="exposeSessionAttributes" value="true" />

但是在DEBUG项目的时候,又发现在通用的视图控制器里面有这么一段

private void buildModelMap(HttpServletRequest request,ModelMap modelMap){
        Enumeration<String> paramNames = request.getParameterNames();
        while (paramNames.hasMoreElements()) {  
            String paramName =  paramNames.nextElement();
            String[] paramValues = request.getParameterValues(paramName);
            if (paramValues.length == 1) {  
                String paramValue = paramValues[0];  
                if (paramValue.length() != 0) {  
                      modelMap.put(paramName, paramValue);  
                }  
            }
        }
    }

很明显能看出这段代码所做的工作就是把request里面的属性放到model里面。在执行完这么一段之后,SpringMVC的视图解析器再将request或者session的信息往model里面put的时候就会报key重复的错误。

解决方案

解决方案是在springMVC的视图解析器里面加上配置告诉解析器可以允许属性覆写。

<property name="allowSessionOverride" value="true"/>
<property name="allowSessionOverride" value="true"/>

源码级别分析

虽然有解决方案,但是为什么再次往model里面放置属性不是直接覆盖呢?VelocityLayoutViewResolver给我们提供的exposeRequestAttributes属性在什么地方起作用?这些问题还没有得到解决。因此我们还需要到Spring的源码里面看看是怎么回事。
通过搜索关键字很容易看到AbstractTemplateView类里面有这么一段

protected final void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        String attribute;
        Object attributeValue;
        if (this.exposeRequestAttributes) {
            for(Enumeration en = request.getAttributeNames(); en.hasMoreElements(); model.put(attribute, attributeValue)) {
                attribute = (String)en.nextElement();
                if (model.containsKey(attribute) && !this.allowRequestOverride) {
                    throw new ServletException("Cannot expose request attribute '" + attribute + "' because of an existing model object of the same name");
                }

                attributeValue = request.getAttribute(attribute);
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Exposing request attribute '" + attribute + "' with value [" + attributeValue + "] to model");
                }
            }
        }

当model里面已经有这个attribute,然后allowRequestOverride又为false(默认值)的话,就抛出ServletException。这就是问题关键所在。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值