17

在 Spring MVC 中,假设我使用 @SessionAttribute 标签定义了一个 SessionAttribute,如下所示:

@SessionAttributes(value = "myModel")
public class MyController{
   ...
}

假设我忘记在 SessionStatus 上调用 status.setComplete(),如下所示:

@RequestMapping(method = RequestMethod.POST)
public void doSomething(@ModelAttribute("myModel") MyModel model, SessionStatus status){
   ...
   //status.setComplete(); <-- Never gets called
}

模型会永远留在会话中吗?它会被清理掉,还是会随着用户浏览网站而变得越来越大?

4

3 回答 3

22

关于控制器退出后是否清除会话属性存在很大的争议。

为了一劳永逸地澄清,我们可以查看Spring MVC 3.1.0 RELEASE源代码。

org.springframework.web.bind.support.SessionAttributeStore接口公开了以下方法:

void storeAttribute(WebRequest request, String attributeName, Object attributeValue);

Object retrieveAttribute(WebRequest request, String attributeName);

void cleanupAttribute(WebRequest request, String attributeName);

默认实现是org.springframework.web.bind.support.DefaultSessionAttributeStore

通过在 Eclipse 中对cleanupAttribute()执行“ Open Call Hierarchy ” ,我们可以看到该方法被 2 个不同的流程调用:

1) org.springframework.web.method.annotation.ModelFactory

public void updateModel(NativeWebRequest request, ModelAndViewContainer mavContainer) throws Exception {

        if (mavContainer.getSessionStatus().isComplete()){
            this.sessionAttributesHandler.cleanupAttributes(request);
        }
        else {
            this.sessionAttributesHandler.storeAttributes(request, mavContainer.getModel());
        }

        if (!mavContainer.isRequestHandled()) {
            updateBindingResult(request, mavContainer.getModel());
        } 
    }

2) org.springframework.web.bind.annotation.support.HandlerMethodInvoker

public final void updateModelAttributes(Object handler, Map<String, Object> mavModel,
            ExtendedModelMap implicitModel, NativeWebRequest webRequest) throws Exception {

        if (this.methodResolver.hasSessionAttributes() && this.sessionStatus.isComplete()) {
            for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {
                this.sessionAttributeStore.cleanupAttribute(webRequest, attrName);
            }
        }
...
}

很明显,在这两种情况下,只有在调用 this.sessionStatus.isComplete()时才会删除 session 属性。

我研究了DefaultSessionAttributeStore的代码。在底层,它获取真正的HTTP Session对象来存储属性,因此它们可能会被同一会话中的其他控制器访问。

所以不,在干净的 POST 之后不会删除会话属性。

于 2012-02-17T15:54:50.670 回答
12

编辑#2:请注意,此答案不再正确。请参阅下面的@doanduyhai 的答案

编辑:请注意,这适用于 Spring 2.5 并且可能,但不一定确保 Spring 3.x 也是如此。仔细检查文档!

这与@Gandalf 所说的一致。

表单控制器对表单请求的生命周期进行建模,从最初查看表单到提交表单。提交表单后,表单控制器的工作就完成了,它将从会话中删除命令对象。

因此,要将命令对象保留在表单工作流之间的会话中,您需要手动管理会话。在干净的 POST 之后,该对象将从会话中删除。

简而言之,我相信 setComplete() 方法只是一种很好的做法,但不一定是必需的。

编辑:我刚刚查看了我的 Spring book 来确认这一点。我将引用它:

当不使用@SessionAttribute 时,将在每个请求上创建一个新的命令对象,即使由于绑定错误而再次呈现表单时也是如此。如果启用此注释,命令对象将存储在会话中以供后续使用,直到表单成功完成。然后这个命令对象将从会话中清除。 这通常在命令对象是一个持久对象时使用,该对象需要在不同请求之间保持相同以跟踪更改。

基本上这就是我上面所说的。它将它存储在会话中,直到您 A) 调用 setComplete() 或 B) 控制器成功完成 POST。

于 2009-06-01T21:58:32.823 回答
1

你有什么理由想要这样做吗?

来自这个线程:@SessionAttribute 问题

@SessionAttributes 的工作方式与 SimpleFormController 的 sessionForm 相同。在第一个请求和最后一个请求之间的持续时间内(大部分时间是初始 GET 和最终 POST),它将命令(或 @SessionAttributes 的任何对象)放入会话中。之后,这些东西被删除。

于 2009-05-19T15:09:55.573 回答