繁体   English   中英

Spring MVC中@SessionAttributes对象的生命周期和注入

[英]Life span and injection of @SessionAttributes object in Spring MVC

我不清楚通过 Spring Boot 2.3.3.RELEASE 在 Spring MVC 中使用@SessionAttributes一些微妙之处。

  • 我有两个控制器, Step1ControllerStep2Controller
  • 两个控制器都在类级别使用@SessionAttributes("foobar")
  • Step1Controller其请求处理过程中@PostMapping增加了一个特殊FooBar实例来使用模型model.addAttribute("foobar", new FooBar("foo", "bar"))
  • Step2Controller在完全独立的 HTTP POST下调用,使用doSomething(FooBar fooBar)在其@PostMapping服务方法中获取FooBar实例。
  • 这所有的话都很棒!

但我不清楚它为什么起作用的一些细节。

@SessionAttributes API 文档部分说明:

一旦处理程序指示其会话会话完成,这些属性将被删除。 因此,将此功能用于此类会话属性,这些属性应该在特定处理程序的会话过程中临时存储在会话中。 对于永久会话属性,例如用户身份验证对象,请改用传统的session.setAttribute方法。

  1. 如果@SessionAttributes仅在 HTTP 会话中临时存储模型属性并在会话结束时将其删除,为什么foobar仍然出现在对Step2Controller的请求中? 在我看来,它仍然在会议中。 我不明白文档在提到“临时”和“处理程序的对话”时是什么意思。 看起来foobar正常存储在会话中。
  2. 看起来,只要在Step1Controller上使用@SessionAttributes("foobar") ,Spring 就会在处理请求后自动将foobar从模型复制到会话中。 这在文档中有所暗示,但我只有通过实验才明白这一点。
  3. 看起来,通过将@SessionAttributes("foobar")放在Step2Controller ,Spring 会在请求之前foobar会话复制模型。 从文档中我根本不清楚这一点。
  4. 最后,请注意,在Step2Controller.doSomething(FooBar fooBar) ,除了@SessionAttributes("foobar")之外,我在FooBar参数上没有任何注释(但这是在控制器类上)。 文档似乎表明我需要向方法参数添加@ModelAttribute注释,例如Step2Controller.doSomething(@ModelAttribute("foobar") FooBar fooBar)或至少Step2Controller.doSomething(@ModelAttribute FooBar fooBar) 但是 Spring 似乎仍然找到了 session 变量,即使在参数上根本没有注释。 为什么? 我怎么会知道这个?

这是关于 Spring 一直困扰我的事情之一:太多事情“神奇地”发生,没有明确的文档说明预期会发生什么。 我想那些使用 Spring 多年的人只是对什么有效什么无效有一种“感觉”; 但是查看代码的新开发人员只需要相信它神奇地完成了它应该做的事情。

有人可以澄清为什么我所描述的有效,尤其是在第一个问题上启发了我吗? 也许这样我也可以培养这种“春天的感觉”,本能地知道要唤起哪些咒语。 谢谢你。

这个答案有两部分

  1. 提供一些关于SessionAttributes一般信息
  2. 通过问题本身

Spring 中的@SessionAttributes

@SessionAttributes的 javadoc声明它应该用于临时存储属性:

during the course of a specific handler's conversation.将此功能用于此类会话属性,这些属性应该在特定处理程序的会话过程中存储在会话中。

这种“对话”的时间边界由程序员明确定义,或者更准确地说:程序员定义对话的完成,他们可以通过SessionStatus 是文档和示例的相关部分:

在第一个请求中,当一个名为pet的模型属性被添加到模型中时,它会自动提升并保存在 HTTP Servlet 会话中。 它保持在那里,直到另一个控制器方法使用SessionStatus方法参数来清除 storage ,如以下示例所示:

@Controller
@SessionAttributes("pet") 
public class EditPetForm {
    @PostMapping("/pets/{id}")
    public String handle(Pet pet, BindingResult errors, SessionStatus status) {
        if (errors.hasErrors) {
            // ...
        }
        status.setComplete(); 
        // ...
    }
}

如果你想深入挖掘,你可以研究以下源代码:

通过问题

  1. 如果@SessionAttributes仅在 HTTP 会话中临时存储模型属性并在会话结束时将其删除,为什么foobar仍然出现在对Step2Controller的请求中?

因为,很可能您还没有定义对话完成。

在我看来,它仍然在会议中。

确切地

我不明白文档在提到“临时”和“处理程序的对话”时是什么意思。

它与Spring WebFlow 有某种关系。 (见这篇介绍性文章)

看起来foobar正常存储在会话中。

是的,请参阅DefaultSessionAttributeStore

您可能会在这里问:什么使某些会话属性具有时间性,而有些则不是? 它们是如何区分的? . 答案可以在源代码中找到:

SessionAttributesHandler.java #L146

/**
 * Remove "known" attributes from the session, i.e. attributes listed
 * by name in {@code @SessionAttributes} or attributes previously stored
 * in the model that matched by type.
 * @param request the current request
 */
public void cleanupAttributes(WebRequest request) {
    for (String attributeName : this.knownAttributeNames) {
        this.sessionAttributeStore.cleanupAttribute(request, attributeName);
    }
}
  1. 看起来,只要在Step1Controller上使用@SessionAttributes("foobar") ,Spring 就会在处理请求后自动将foobar从模型复制到会话中。

是的,它会

  1. 看起来,通过将@SessionAttributes("foobar")放在Step2Controller ,Spring 会在请求之前foobar会话复制模型。

也是真的

  1. 最后,请注意,在Step2Controller.doSomething(FooBar fooBar) ,除了@SessionAttributes("foobar")之外,我在FooBar参数上没有任何注释(但这是在控制器类上)。 文档似乎表明我需要向方法参数添加@ModelAttribute注释,例如Step2Controller.doSomething(@ModelAttribute("foobar") FooBar fooBar)或至少Step2Controller.doSomething(@ModelAttribute FooBar fooBar) 但是 Spring 似乎仍然找到了 session 变量,即使在参数上根本没有注释。 为什么? 我怎么会知道这个?

请参阅方法参数部分:

如果方法参数与此表中的任何早期值都不匹配,并且它是简单类型(由 BeanUtils#isSimpleProperty 确定,则将其解析为 @RequestParam。否则,将其解析为 @ModelAttribute。

这是关于 Spring 一直困扰我的事情之一:太多事情“神奇地”发生,没有明确的文档说明预期会发生什么。 我想那些使用 Spring 多年的人只是对什么有效什么无效有一种“感觉”; 但是查看代码的新开发人员只需要相信它神奇地完成了它应该做的事情。

在这里,我建议阅读参考文档,它可以提供线索如何描述 Spring 的某些特定行为


10/11/2020 更新

Denis,这种自动应用模型中的参数作为方法参数的能力是否只适用于接口? 我发现如果 FooBar 是一个接口, Step2Controller.doSomething(FooBar fooBar) 的工作原理如上所述。 但是如果 FooBar 是一个类,即使我在模型中有一个 FooBar 的实例, Step2Controller.doSomething(FooBar fooBar) 也会导致“找不到类 FooBar 的主要或默认构造函数”异常。 甚至@ModelAttribute 也行不通。 我必须使用@ModelAttribute("foobar")。 为什么类在参数替换中与接口的工作方式不同?

在我看来,命名/ @SessionAttributes#names存在一些问题。

我创建了一个示例项目来演示问题可能隐藏在哪里。

该项目分为两部分:

  1. 尝试使用类
  2. 尝试使用接口

项目的入口点是两个测试(参见ClassFooBarControllerTestInterfaceFooBarControllerTest

我留下评论来解释这里发生的事情

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM